kernel/generic: Restore kernel files for v6.6
authorMieczyslaw Nalewaj <namiltd@yahoo.com>
Mon, 16 Dec 2024 07:13:42 +0000 (08:13 +0100)
committerZoltan HERPAI <wigyori@uid0.hu>
Tue, 17 Dec 2024 22:28:09 +0000 (23:28 +0100)
This is an automatically generated commit which aids following Kernel patch
history, as git will see the move and copy as a rename thus defeating the
purpose.

For the original discussion see:
https://lists.openwrt.org/pipermail/openwrt-devel/2023-October/041673.html

Signed-off-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
507 files changed:
target/linux/generic/backport-6.6/0080-v6.9-smp-Avoid-setup_max_cpus_namespace_collision_shadowing.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/065-v6.10-compiler_types.h-Define-__retain.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/066-v6.10-bpf-Harden-__bpf_kfunc-against-linker-removal.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/192-v6.12-fix-libbpf-Wmaybe-uninitialized.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/300-v6.7-arm64-swiotlb-Reduce-the-default-size-if-no-ZONE_DMA.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/310-v6.7-mips-kexec-fix-the-incorrect-ifdeffery-and-dependenc.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/320-v6.11-mips-bmips-rework-and-cache-CBR-addr-handling.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/321-v6.11-mips-bmips-setup-make-CBR-address-configurable.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/322-v6.11-mips-bmips-enable-RAC-on-BMIPS4350.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/400-v6.9-mtd-rawnand-brcmnand-Support-write-protection-settin.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/401-v6.9-dt-bindings-mtd-add-basic-bindings-for-UBI.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/402-v6.9-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/403-v6.9-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/404-v6.9-mtd-ubi-attach-from-device-tree.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/405-v6.9-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/406-v6.9-mtd-ubi-populate-ubi-volume-fwnode.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/407-v6.9-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/408-v6.9-mtd-ubi-fix-NVMEM-over-UBI-volumes-on-32-bit-systems.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/410-v6.13-01-block-add-support-for-defining-read-only-partitions.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/410-v6.13-03-block-introduce-add_disk_fwnode.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/410-v6.13-04-mmc-block-attach-partitions-fwnode-if-found-in-mmc-c.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/410-v6.13-05-block-add-support-for-partition-table-defined-in-OF.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/411-v6.7-mtd-spinand-add-support-for-FORESEE-F35SQA002G.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/412-v6.14-mtd-spinand-add-support-for-FORESEE-F35SQA001G.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/600-v6.10-net-Remove-conditional-threaded-NAPI-wakeup-based-on.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/601-v6.10-net-Allow-to-use-SMP-threads-for-backlog-NAPI.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/602-v6.10-net-Use-backlog-NAPI-to-clean-up-the-defer_list.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/603-v6.10-net-Rename-rps_lock-to-backlog_lock.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/610-v6.9-net-mdio-add-2.5g-and-5g-related-PMA-speed-constants.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/611-01-v6.11-udp-Allow-GSO-transmit-from-devices-with-no-checksum.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/611-02-v6.11-net-Make-USO-depend-on-CSUM-offload.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/611-03-v6.11-udp-Fall-back-to-software-USO-if-IPv6-extension-head.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/612-v6.9-net-get-stats64-if-device-if-driver-is-configured.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/700-v6.10-net-create-a-dummy-net_device-allocator.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/718-v6.9-net-phy-qcom-at803x-fix-kernel-panic-with-at8031_pro.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/722-v6.10-dt-bindings-arm-qcom-ids-Add-SoC-ID-for-IPQ5321.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/734-v6.8-net-phy-bcm54612e-add-suspend-resume.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/740-v6.10-net-stmmac-dwmac-ipq806x-account-for-rgmii-txid-rxid.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/751-01-STABLE-net-ethernet-mediatek-split-tx-and-rx-fields-in-mtk_.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/751-02-STABLE-net-ethernet-mediatek-use-QDMA-instead-of-ADMAv2-on-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-21-v6.7-net-ethernet-mtk_wed-fix-firmware-loading-for-MT7986.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-22-v6.7-net-ethernet-mtk_wed-remove-wo-pointer-in-wo_r32-wo_.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-23-v6.8-net-ethernet-mtk_wed-rely-on-__dev_alloc_page-in-mtk.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-24-v6.8-net-ethernet-mtk_wed-add-support-for-devices-with-mo.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-25-v6.10-net-ethernet-mtk_eth_soc-handle-dma-buffer-size-soc-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-26-v6.10-net-ethernet-mtk_eth_soc-ppe-add-support-for-multipl.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-27-v6.10-net-ethernet-mtk_eth_soc-ppe-prevent-ppe-update-for-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-28-v6.10-net-ethernet-mediatek-Allow-gaps-in-MAC-allocation.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-29-v6.10-net-ethernet-mtk_ppe-Change-PPE-entries-number-to-16.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/752-30-v6.10-net-ethernet-mtk_eth_soc-implement-.-get-set-_pausep.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/763-v6.10-net-dsa-introduce-dsa_phylink_to_port.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/764-v6.10-net-dsa-allow-DSA-switch-drivers-to-provide-their-ow.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/765-v6.9-net-phy-aquantia-add-support-for-AQR114C-PHY-ID.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/770-net-introduce-napi_is_scheduled-helper.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-01-v6.8-r8169-improve-RTL8411b-phy-down-fixup.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-02-v6.8-r8169-remove-not-needed-check-in-rtl_fw_write_firmwa.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-03-v6.8-r8169-remove-multicast-filter-limit.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-04-v6.8-r8169-improve-handling-task-scheduling.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-05-v6.8-r8169-add-support-for-LED-s-on-RTL8168-RTL8101.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-06-v6.8-r8169-fix-building-with-CONFIG_LEDS_CLASS-m.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-07-v6.9-r8169-simplify-EEE-handling.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-08-v6.9-r8169-add-support-for-RTL8126A.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-09-v6.9-r8169-improve-checking-for-valid-LED-modes.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-10-v6.9-r8169-simplify-code-by-using-core-provided-pcpu-stat.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-11-v6.9-r8169-add-LED-support-for-RTL8125-RTL8126.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-12-v6.9-r8169-add-generic-rtl_set_eee_txidle_timer-function.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-13-v6.9-r8169-support-setting-the-EEE-tx-idle-timer-on-RTL81.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-14-v6.9-r8169-add-support-for-returning-tx_lpi_timer-in-etht.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-15-v6.9-r8169-add-MODULE_FIRMWARE-entry-for-RTL8126A.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-16-v6.9-r8169-fix-LED-related-deadlock-on-module-removal.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-17-v6.9-r8169-add-missing-conditional-compiling-for-call-to-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-18-v6.10-r8169-add-support-for-RTL8168M.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-19-v6.10-net-annotate-writes-on-dev-mtu-from-ndo_change_mtu.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-20-v6.11-r8169-disable-interrupt-source-RxOverflow.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-21-v6.11-r8169-remove-detection-of-chip-version-11-early-RTL8.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-22-v6.12-r8169-add-support-for-RTL8126A-rev.b.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/780-23-v6.12-r8169-add-missing-MODULE_FIRMWARE-entry-for-RTL8126A.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-01-v6.9-net-phy-realtek-add-support-for-RTL8126A-integrated-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-02-v6.9-net-phy-realtek-use-generic-MDIO-constants.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-03-v6.9-net-phy-realtek-add-5Gbps-support-to-rtl822x_config_.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-04-v6.9-net-phy-realtek-use-generic-MDIO-helpers-to-simplify.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-05-v6.10-net-phy-realtek-configure-SerDes-mode-for-rtl822xb-P.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-06-v6.10-net-phy-realtek-add-get_rate_matching-for-rtl822xb-P.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-07-v6.10-net-phy-realtek-Add-driver-instances-for-rtl8221b-vi.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-08-v6.10-net-phy-realtek-Change-rtlgen_get_speed-to-rtlgen_de.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-09-v6.10-net-phy-realtek-add-rtl822x_c45_get_features-to-set-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-10-v6.11-net-phy-realtek-add-support-for-rtl8224-2.5Gbps-PHY.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-11-v6.11-net-phy-realtek-Add-support-for-PHY-LEDs-on-RTL8211F.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-12-v6.11-net-phy-realtek-Fix-setting-of-PHY-LEDs-Mode-B-bit-o.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-13-v6.12-net-phy-realtek-Check-the-index-value-in-led_hw_cont.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-14-v6.12-net-phy-realtek-Fix-MMD-access-on-RTL8126A-integrate.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-15-v6.13-net-phy-realtek-read-duplex-and-gbit-master-from-PHY.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-16-v6.13-net-phy-realtek-change-order-of-calls-in-C22-read_st.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/781-17-v6.13-net-phy-realtek-clear-1000Base-T-link-partner-advert.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-01-v6.7-net-dsa-mt7530-Convert-to-platform-remove-callback-r.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-02-v6.7-net-dsa-mt753x-remove-mt753x_phylink_pcs_link_up.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-03-v6.7-net-dsa-mt7530-replace-deprecated-strncpy-with-ethto.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-04-v6.9-net-dsa-mt7530-support-OF-based-registration-of-swit.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-05-v6.9-net-dsa-mt7530-always-trap-frames-to-active-CPU-port.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-06-v6.9-net-dsa-mt7530-use-p5_interface_select-as-data-type-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-07-v6.9-net-dsa-mt7530-store-port-5-SGMII-capability-of-MT75.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-08-v6.9-net-dsa-mt7530-improve-comments-regarding-switch-por.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-09-v6.9-net-dsa-mt7530-improve-code-path-for-setting-up-port.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-10-v6.9-net-dsa-mt7530-do-not-set-priv-p5_interface-on-mt753.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-11-v6.9-net-dsa-mt7530-do-not-run-mt7530_setup_port5-if-port.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-12-v6.9-net-dsa-mt7530-empty-default-case-on-mt7530_setup_po.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-13-v6.9-net-dsa-mt7530-move-XTAL-check-to-mt7530_setup.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-14-v6.9-net-dsa-mt7530-simplify-mt7530_pad_clk_setup.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-15-v6.9-net-dsa-mt7530-call-port-6-setup-from-mt7530_mac_con.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-16-v6.9-net-dsa-mt7530-remove-pad_setup-function-pointer.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-17-v6.9-net-dsa-mt7530-correct-port-capabilities-of-MT7988.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-18-v6.9-net-dsa-mt7530-do-not-clear-config-supported_interfa.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-19-v6.9-net-dsa-mt7530-remove-.mac_port_config-for-MT7988-an.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-20-v6.9-net-dsa-mt7530-set-interrupt-register-only-for-MT753.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-21-v6.9-net-dsa-mt7530-do-not-use-SW_PHY_RST-to-reset-MT7531.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-22-v6.9-net-dsa-mt7530-get-rid-of-useless-error-returns-on-p.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-23-v6.9-net-dsa-mt7530-get-rid-of-priv-info-cpu_port_config.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-24-v6.9-net-dsa-mt7530-get-rid-of-mt753x_mac_config.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-25-v6.9-net-dsa-mt7530-put-initialising-PCS-devices-code-bac.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-26-v6.9-net-dsa-mt7530-sort-link-settings-ops-and-force-link.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-27-v6.9-net-dsa-mt7530-simplify-link-operations.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-28-v6.9-net-dsa-mt7530-disable-LEDs-before-reset.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-30-v6.9-net-dsa-mt7530-prevent-possible-incorrect-XTAL-frequ.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-33-v6.10-net-dsa-mt7530-provide-own-phylink-MAC-operations.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-36-v6.10-net-dsa-mt7530-mdio-read-PHY-address-of-switch-from-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-37-v6.10-net-dsa-mt7530-simplify-core-operations.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-38-v6.10-net-dsa-mt7530-disable-EEE-abilities-on-failure-on-M.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-39-v6.10-net-dsa-mt7530-refactor-MT7530_PMCR_P.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-40-v6.10-net-dsa-mt7530-rename-p5_intf_sel-and-use-only-for-M.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-41-v6.10-net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-42-v6.10-net-dsa-mt7530-refactor-MT7530_MFC-and-MT7531_CFC-ad.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-43-v6.10-net-dsa-mt7530-refactor-MT7530_HWTRAP-and-MT7530_MHW.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-44-v6.10-net-dsa-mt7530-move-MT753X_MTRAP-operations-for-MT75.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-45-v6.10-net-dsa-mt7530-return-mt7530_setup_mdio-mt7531_setup.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-46-v6.10-net-dsa-mt7530-define-MAC-speed-capabilities-per-swi.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-47-v6.10-net-dsa-mt7530-get-rid-of-function-sanity-check.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-48-v6.10-net-dsa-mt7530-refactor-MT7530_PMEEECR_P.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-49-v6.10-net-dsa-mt7530-get-rid-of-mac_port_validate-member-o.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-50-v6.10-net-dsa-mt7530-use-priv-ds-num_ports-instead-of-MT75.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-51-v6.10-net-dsa-mt7530-do-not-pass-port-variable-to-mt7531_r.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-52-v6.10-net-dsa-mt7530-explain-exposing-MDIO-bus-of-MT7531AE.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-53-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-54-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-55-v6.11-net-dsa-mt7530-factor-out-bridge-join-leave-logic.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/790-56-v6.11-net-dsa-mt7530-add-support-for-bridge-port-isolation.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/791-v6.11-01-net-phy-aquantia-move-priv-and-hw-stat-to-header.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/791-v6.11-02-net-phy-aquantia-add-support-for-PHY-LEDs.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/795-v6.7-16-r8152-use-napi_gro_frags.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/800-v6.9-0001-dt-bindings-leds-Add-FUNCTION-defines-for-per-band-W.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/800-v6.9-0002-dt-bindings-leds-Add-LED_FUNCTION_WAN_ONLINE-for-Int.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/810-v6.11-hwmon-g672-add-support-for-g761.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/816-v6.7-0003-nvmem-Use-device_get_match_data.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/817-v6.9-of-property-Make-no-port-node-found-output-a-debug.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/818-v6.8-of-device-Export-of_device_make_bus_id.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/820-v6.9-0002-nvmem-mtk-efuse-Register-MediaTek-socinfo-driver-fro.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/820-v6.9-0003-nvmem-zynqmp_nvmem-zynqmp_nvmem_probe-cleanup.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/820-v6.9-0004-nvmem-zynqmp_nvmem-Add-support-to-access-efuse.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/820-v6.9-0005-nvmem-mtk-efuse-Drop-NVMEM-device-name.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/820-v6.9-0006-nvmem-core-make-nvmem_layout_bus_type-const.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/820-v6.9-0007-nvmem-core-Print-error-on-wrong-bits-DT-property.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/821-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/821-v6.10-0002-nvmem-layouts-onie-tlv-drop-driver-owner-initializat.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/821-v6.10-0003-nvmem-layouts-sl28vpd-drop-driver-owner-initializati.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/821-v6.10-0004-nvmem-sc27xx-fix-module-autoloading.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/821-v6.10-0005-nvmem-sprd-fix-module-autoloading.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/821-v6.10-0006-nvmem-core-switch-to-use-device_add_groups.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/821-v6.10-0007-nvmem-lpc18xx_eeprom-Convert-to-platform-remove-call.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/821-v6.10-0008-nvmem-meson-mx-efuse-Remove-nvmem_device-from-efuse-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/822-v6.11-0001-nvmem-add-missing-MODULE_DESCRIPTION-macros.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/822-v6.11-0002-nvmem-meson-efuse-Replacing-the-use-of-of_node_put-t.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/822-v6.11-0004-nvmem-rockchip-otp-Set-type-to-OTP.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/822-v6.11-0005-nvmem-rockchip-efuse-set-type-to-OTP.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/822-v6.11-0006-nvmem-core-add-single-sysfs-group.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/822-v6.11-0007-nvmem-core-remove-global-nvmem_cells_group.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/822-v6.11-0008-nvmem-core-drop-unnecessary-range-checks-in-sysfs-ca.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/822-v6.11-0009-nvmem-Use-sysfs_emit-for-type-attribute.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/822-v6.11-0010-nvmem-core-Implement-force_ro-sysfs-attribute.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/823-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/823-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/823-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/836-v6.9-net-phy-aquantia-clear-PMD-Global-Transmit-Disable-b.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/837-v6.12-net-phy-aquantia-fix-setting-active_low-bit.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/838-v6.12-net-phy-aquantia-fix-applying-active_low-bit-after-reset.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/839-v6.13-net-phy-aquantia-allow-forcing-order-of-MDI-pairs.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/840-v6.13-net-phy-aquantia-fix-return-value-check-in-aqr107_co.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/842-v6.13-net-phy-aquantia-correctly-describe-LED-polarity-ove.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/843-v6.13-net-phy-mxl-gpy-add-basic-LED-support.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/844-v6.13-net-phy-mxl-gpy-add-missing-support-for-TRIGGER_NETD.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/845-v6.13-net-phy-mxl-gpy-correctly-describe-LED-polarity.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/846-v6.13-net-phy-intel-xway-add-support-for-PHY-LEDs.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/850-v6.8-bus-mhi-host-Add-a-separate-timeout-parameter-for-wa.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/851-v6.8-bus-mhi-host-pci_generic-Add-SDX75-based-modem-suppo.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/852-stable-bus-mhi-host-pci_generic-constify-modem_telit_fn980_.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/853-v6.10-bus-mhi-host-Add-mhi_power_down_keep_dev-API-to-supp.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/860-v6.7-leds-add-ktd202x-driver.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/861-v6.10-leds-rgb-leds-ktd202x-get-device-properties-through-fwnode.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/862-v6.10-leds-rgb-leds-ktd202x-i2c-id-tables-for-ktd2026-and-2027.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/863-v6.10-leds-rgb-leds-ktd202x-initialize-mutex-earlier.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/894-v6.8-net-ethtool-implement-ethtool_puts.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/895-02-v6.8-net-phylink-use-for_each_set_bit.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/895-03-v6.8-net-phylink-split-out-per-interface-validation.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/895-04-v6.8-net-phylink-pass-PHY-into-phylink_validate_one.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/895-05-v6.8-net-phylink-pass-PHY-into-phylink_validate_mask.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/895-06-v6.8-net-phylink-split-out-PHY-validation-from-phylink_br.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/895-07-v6.8-net-phylink-use-the-PHY-s-possible_interfaces-if-pop.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-01-v6.9-net-dsa-mv88e6xxx-rename-mv88e6xxx_g2_scratch_gpio_s.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/896-02-v6.9-net-dsa-mv88e6xxx-add-Amethyst-specific-SMI-GPIO-fun.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/897-01-v6.9-net-phy-qcom-qca808x-add-helper-for-checking-for-1G-.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/897-02-v6.9-net-phy-qcom-qca808x-fill-in-possible_interfaces.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/898-v6.7-mtd-spinand-winbond-add-support-for-serial-NAND-flash.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/899-v6.9-mtd-spinand-winbond-add-W25N04KV.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/900-v6.11-net-free_netdev-exit-earlier-if-dummy.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/901-v6.13-net-dsa-mv88e6xxx-Support-LED-control.patch [new file with mode: 0644]
target/linux/generic/config-6.6 [new file with mode: 0644]
target/linux/generic/hack-6.6/200-tools_portability.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/204-module_strip.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/205-kconfig-abort-configuration-on-unset-symbol.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/210-darwin_scripts_include.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/211-darwin-uuid-typedef-clash.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/214-spidev_h_portability.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/220-arm-gc_sections.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/230-openwrt_lzma_options.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/250-netfilter_depends.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/251-kconfig.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/253-ksmbd-config.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/259-regmap_dynamic.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/260-crypto_test_dependencies.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/261-lib-arc4-unhide.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/280-rfkill-stubs.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/402-mtd-blktrans-call-add-disks-after-mtd-device.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/430-mtk-bmt-support.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/600-net-enable-fraglist-GRO-by-default.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/645-netfilter-connmark-introduce-set-dscpmark.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/650-netfilter-add-xt_FLOWOFFLOAD-target.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/651-wireless_mesh_header.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/660-fq_codel_defaults.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/661-kernel-ct-size-the-hashtable-more-adequately.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/721-net-add-packet-mangeling.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/723-net-phy-aquantia-fix-system-side-protocol-mi.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/760-net-usb-r8152-add-LED-configuration-from-OF.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/761-dt-bindings-net-add-RTL8152-binding-documentation.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/765-mxl-gpy-control-LED-reg-from-DT.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/773-bgmac-add-srab-switch.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/780-usb-net-MeigLink_modem_support.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/781-usb-net-rndis-support-asr.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/800-GPIO-add-named-gpio-exports.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/810-bcma-ssb-fallback-sprom.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/901-debloat_sock_diag.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/902-debloat_proc.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/904-debloat_dma_buf.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/910-kobject_uevent.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/911-kobject_add_broadcast_uevent.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/920-device_tree_cmdline.patch [new file with mode: 0644]
target/linux/generic/hack-6.6/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/103-kbuild-export-SUBARCH.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/141-jffs2-add-RENAME_EXCHANGE-support.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/142-jffs2-add-splice-ops.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/150-bridge_allow_receiption_on_disabled_port.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/190-rtc-rs5c372-support_alarms_up_to_1_week.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/203-kallsyms_uncompressed.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/205-backtrace_module_info.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/240-remove-unsane-filenames-from-deps_initramfs-list.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/270-platform-mikrotik-build-bits.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/300-mips_expose_boot_raw.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/302-mips_no_branch_likely.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/305-mips_module_reloc.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/308-mips32r2_tune.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/310-arm_module_unresolved_weak_sym.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/332-arc-add-OWRTDTB-section.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/333-arc-enable-unaligned-access-in-kernel-mode.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/350-mips-kernel-fix-detect_memory_region-function.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/400-mtd-mtdsplit-support.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/410-mtd-spinand-set-bitflip_threshold-to-75-of-ECC-strength.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/420-mtd-redboot_space.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/430-mtd-add-myloader-partition-parser.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/435-mtd-add-routerbootpart-parser-config.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/450-dt-bindings-block-add-basic-bindings-for-block-devic.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/451-block-partitions-populate-fwnode.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/452-block-add-support-for-notifications.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/453-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/454-nvmem-implement-block-NVMEM-provider.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/455-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/456-mmc-core-set-card-fwnode_handle.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/457-mmc-block-set-fwnode-of-disk-devices.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/458-mmc-block-set-GENHD_FL_NVMEM.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/465-m25p80-mx-disable-software-protection.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/476-mtd-spi-nor-add-eon-en25q128.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/477-mtd-spi-nor-add-eon-en25qx128a.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/479-mtd-spi-nor-add-xtx-xt25f128b.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/482-mtd-spi-nor-add-gd25q512.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/484-mtd-spi-nor-add-esmt-f25l16pa.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/485-mtd-spi-nor-add-xmc-xm25qh128c.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/488-mtd-spi-nor-add-xmc-xm25qh64c.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/489-mtd-spinand-winbond-add-support-for-W25N01KV.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/491-ubi-auto-create-ubiblock-device-for-rootfs.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/494-mtd-ubi-add-EOF-marker-support.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/500-fs_cdrom_dependencies.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/511-init-bypass-device-lookup-for-dev-fit-rootfs.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/530-jffs2_make_lzma_available.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/532-jffs2_eofdetect.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/600-netfilter_conntrack_flush.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/610-netfilter_match_bypass_default_checks.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/611-netfilter_match_bypass_default_table.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/612-netfilter_match_reduce_memory_access.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/620-net_sched-codel-do-not-defer-queue-length-update.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/630-packet_socket_type.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/640-net-bridge-fix-switchdev-host-mdb-entry-updates.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/641-net-bridge-switchdev-Don-t-drop-packets-between-port.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/655-increase_skb_pad.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/680-net-add-TCP-fraglist-GRO-support.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/681-net-remove-NETIF_F_GSO_FRAGLIST-from-NETIF_F_GSO_SOF.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/685-net-gso-fix-tcp-fraglist-segmentation-after-pull-fro.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/704-netfilter-nf_tables-fix-bidirectional-offload-regres.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/720-02-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/720-03-net-phy-realtek-make-sure-paged-read-is-protected-by.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/720-04-net-phy-realtek-introduce-rtl822x_probe.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/730-net-ethernet-mtk_eth_soc-reset-all-TX-queues-on-DMA-.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/732-00-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/732-02-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/733-01-net-ethernet-mtk_eth_soc-use-napi_build_skb.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/734-net-ethernet-mediatek-enlarge-DMA-reserve-buffer.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/735-net-ethernet-mtk_eth_soc-fix-memory-corruption-durin.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/736-net-ethernet-mtk_wed-fix-path-of-MT7988-WO-firmware.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/740-net-phy-motorcomm-Add-missing-include.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/790-bus-mhi-core-add-SBL-state-callback.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/791-tg3-Fix-DMA-allocations-on-57766-devices.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/804-nvmem-core-support-mac-base-fixed-layout-cells.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/810-pci_disable_common_quirks.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/811-pci_disable_usb_common_quirks.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/834-ledtrig-libata.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/840-hwrng-bcm2835-set-quality-to-1000.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/880-01-dt-bindings-leds-add-LED_FUNCTION_MOBILE-for-mobile-.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/880-02-dt-bindings-leds-add-LED_FUNCTION_SPEED_-for-link-sp.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/890-usb-serial-add-support-for-CH348.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/900-net-ag71xx-fix-qca9530-and-qca9550-mdio-probe.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/920-mangle_bootargs.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/980-tools-thermal-tmon-Fix-compilation-warning-for-wrong.patch [new file with mode: 0644]

diff --git a/target/linux/generic/backport-6.6/0080-v6.9-smp-Avoid-setup_max_cpus_namespace_collision_shadowing.patch b/target/linux/generic/backport-6.6/0080-v6.9-smp-Avoid-setup_max_cpus_namespace_collision_shadowing.patch
new file mode 100644 (file)
index 0000000..54ebaa1
--- /dev/null
@@ -0,0 +1,56 @@
+From 4c8a49854130da0117a0fdb858551824919a2389 Mon Sep 17 00:00:00 2001
+From: Ingo Molnar <mingo@kernel.org>
+Date: Tue, 27 Feb 2024 09:58:15 +0100
+Subject: [PATCH] smp: Avoid 'setup_max_cpus' namespace collision/shadowing
+
+bringup_nonboot_cpus() gets passed the 'setup_max_cpus'
+variable in init/main.c - which is also the name of the parameter,
+shadowing the name.
+
+To reduce confusion and to allow the 'setup_max_cpus' value
+to be #defined in the <linux/smp.h> header, use the 'max_cpus'
+name for the function parameter name.
+
+Signed-off-by: Ingo Molnar <mingo@kernel.org>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+Cc: linux-kernel@vger.kernel.org
+---
+ include/linux/cpu.h | 2 +-
+ kernel/cpu.c        | 6 +++---
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/include/linux/cpu.h
++++ b/include/linux/cpu.h
+@@ -109,7 +109,7 @@ void notify_cpu_starting(unsigned int cp
+ extern void cpu_maps_update_begin(void);
+ extern void cpu_maps_update_done(void);
+ int bringup_hibernate_cpu(unsigned int sleep_cpu);
+-void bringup_nonboot_cpus(unsigned int setup_max_cpus);
++void bringup_nonboot_cpus(unsigned int max_cpus);
+ #else /* CONFIG_SMP */
+ #define cpuhp_tasks_frozen    0
+--- a/kernel/cpu.c
++++ b/kernel/cpu.c
+@@ -1905,17 +1905,17 @@ static bool __init cpuhp_bringup_cpus_pa
+ static inline bool cpuhp_bringup_cpus_parallel(unsigned int ncpus) { return false; }
+ #endif /* CONFIG_HOTPLUG_PARALLEL */
+-void __init bringup_nonboot_cpus(unsigned int setup_max_cpus)
++void __init bringup_nonboot_cpus(unsigned int max_cpus)
+ {
+-      if (!setup_max_cpus)
++      if (!max_cpus)
+               return;
+       /* Try parallel bringup optimization if enabled */
+-      if (cpuhp_bringup_cpus_parallel(setup_max_cpus))
++      if (cpuhp_bringup_cpus_parallel(max_cpus))
+               return;
+       /* Full per CPU serialized bringup */
+-      cpuhp_bringup_mask(cpu_present_mask, setup_max_cpus, CPUHP_ONLINE);
++      cpuhp_bringup_mask(cpu_present_mask, max_cpus, CPUHP_ONLINE);
+ }
+ #ifdef CONFIG_PM_SLEEP_SMP
diff --git a/target/linux/generic/backport-6.6/065-v6.10-compiler_types.h-Define-__retain.patch b/target/linux/generic/backport-6.6/065-v6.10-compiler_types.h-Define-__retain.patch
new file mode 100644 (file)
index 0000000..862207f
--- /dev/null
@@ -0,0 +1,58 @@
+From 443df175be581618d6ff781dc3af3aa1a9ba789d Mon Sep 17 00:00:00 2001
+From: Tony Ambardar <Tony.Ambardar@gmail.com>
+Date: Fri, 31 May 2024 23:55:55 -0700
+Subject: [PATCH 1/2] compiler_types.h: Define __retain for
+ __attribute__((__retain__))
+
+Some code includes the __used macro to prevent functions and data from
+being optimized out. This macro implements __attribute__((__used__)), which
+operates at the compiler and IR-level, and so still allows a linker to
+remove objects intended to be kept.
+
+Compilers supporting __attribute__((__retain__)) can address this gap by
+setting the flag SHF_GNU_RETAIN on the section of a function/variable,
+indicating to the linker the object should be retained. This attribute is
+available since gcc 11, clang 13, and binutils 2.36.
+
+Provide a __retain macro implementing __attribute__((__retain__)), whose
+first user will be the '__bpf_kfunc' tag.
+
+Link: https://lore.kernel.org/bpf/ZlmGoT9KiYLZd91S@krava/T/
+Cc: stable@vger.kernel.org # v6.6+
+Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com>
+---
+ include/linux/compiler_types.h | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+--- a/include/linux/compiler_types.h
++++ b/include/linux/compiler_types.h
+@@ -145,6 +145,29 @@ static inline void __chk_io_ptr(const vo
+ #define __has_builtin(x) (0)
+ #endif
++/*
++ * Annotating a function/variable with __retain tells the compiler to place
++ * the object in its own section and set the flag SHF_GNU_RETAIN. This flag
++ * instructs the linker to retain the object during garbage-cleanup or LTO
++ * phases.
++ *
++ * Note that the __used macro is also used to prevent functions or data
++ * being optimized out, but operates at the compiler/IR-level and may still
++ * allow unintended removal of objects during linking.
++ *
++ * Optional: only supported since gcc >= 11, clang >= 13
++ *
++ *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-retain-function-attribute
++ * clang: https://clang.llvm.org/docs/AttributeReference.html#retain
++ */
++#if __has_attribute(__retain__) && \
++      (defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || \
++       defined(CONFIG_LTO_CLANG))
++# define __retain                     __attribute__((__retain__))
++#else
++# define __retain
++#endif
++
+ /* Compiler specific macros. */
+ #ifdef __clang__
+ #include <linux/compiler-clang.h>
diff --git a/target/linux/generic/backport-6.6/066-v6.10-bpf-Harden-__bpf_kfunc-against-linker-removal.patch b/target/linux/generic/backport-6.6/066-v6.10-bpf-Harden-__bpf_kfunc-against-linker-removal.patch
new file mode 100644 (file)
index 0000000..f7e4f43
--- /dev/null
@@ -0,0 +1,65 @@
+From ac507ed9882fd91a94657d68fe9ceac04b957103 Mon Sep 17 00:00:00 2001
+From: Tony Ambardar <Tony.Ambardar@gmail.com>
+Date: Sat, 1 Jun 2024 00:00:21 -0700
+Subject: [PATCH 2/2] bpf: Harden __bpf_kfunc tag against linker kfunc removal
+
+BPF kfuncs are often not directly referenced and may be inadvertently
+removed by optimization steps during kernel builds, thus the __bpf_kfunc
+tag mitigates against this removal by including the __used macro. However,
+this macro alone does not prevent removal during linking, and may still
+yield build warnings (e.g. on mips64el):
+
+    LD      vmlinux
+    BTFIDS  vmlinux
+  WARN: resolve_btfids: unresolved symbol bpf_verify_pkcs7_signature
+  WARN: resolve_btfids: unresolved symbol bpf_lookup_user_key
+  WARN: resolve_btfids: unresolved symbol bpf_lookup_system_key
+  WARN: resolve_btfids: unresolved symbol bpf_key_put
+  WARN: resolve_btfids: unresolved symbol bpf_iter_task_next
+  WARN: resolve_btfids: unresolved symbol bpf_iter_css_task_new
+  WARN: resolve_btfids: unresolved symbol bpf_get_file_xattr
+  WARN: resolve_btfids: unresolved symbol bpf_ct_insert_entry
+  WARN: resolve_btfids: unresolved symbol bpf_cgroup_release
+  WARN: resolve_btfids: unresolved symbol bpf_cgroup_from_id
+  WARN: resolve_btfids: unresolved symbol bpf_cgroup_acquire
+  WARN: resolve_btfids: unresolved symbol bpf_arena_free_pages
+    NM      System.map
+    SORTTAB vmlinux
+    OBJCOPY vmlinux.32
+
+Update the __bpf_kfunc tag to better guard against linker optimization by
+including the new __retain compiler macro, which fixes the warnings above.
+
+Verify the __retain macro with readelf by checking object flags for 'R':
+
+  $ readelf -Wa kernel/trace/bpf_trace.o
+  Section Headers:
+    [Nr]  Name              Type     Address  Off  Size ES Flg Lk Inf Al
+  ...
+    [178] .text.bpf_key_put PROGBITS 00000000 6420 0050 00 AXR  0   0  8
+  ...
+  Key to Flags:
+  ...
+    R (retain), D (mbind), p (processor specific)
+
+Link: https://lore.kernel.org/bpf/ZlmGoT9KiYLZd91S@krava/T/
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/r/202401211357.OCX9yllM-lkp@intel.com/
+Fixes: 57e7c169cd6a ("bpf: Add __bpf_kfunc tag for marking kernel functions as kfuncs")
+Cc: stable@vger.kernel.org # v6.6+
+Signed-off-by: Tony Ambardar <Tony.Ambardar@gmail.com>
+---
+ include/linux/btf.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/linux/btf.h
++++ b/include/linux/btf.h
+@@ -81,7 +81,7 @@
+  * as to avoid issues such as the compiler inlining or eliding either a static
+  * kfunc, or a global kfunc in an LTO build.
+  */
+-#define __bpf_kfunc __used noinline
++#define __bpf_kfunc __used __retain noinline
+ /*
+  * Return the name of the passed struct, if exists, or halt the build if for
diff --git a/target/linux/generic/backport-6.6/192-v6.12-fix-libbpf-Wmaybe-uninitialized.patch b/target/linux/generic/backport-6.6/192-v6.12-fix-libbpf-Wmaybe-uninitialized.patch
new file mode 100644 (file)
index 0000000..638fe4b
--- /dev/null
@@ -0,0 +1,59 @@
+From fab45b962749184e1a1a57c7c583782b78fad539 Mon Sep 17 00:00:00 2001
+From: Sam James <sam@gentoo.org>
+Date: Tue, 13 Aug 2024 20:49:06 +0100
+Subject: [PATCH] libbpf: Workaround -Wmaybe-uninitialized false positive
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In `elf_close`, we get this with GCC 15 -O3 (at least):
+```
+In function ‘elf_close’,
+    inlined from ‘elf_close’ at elf.c:53:6,
+    inlined from ‘elf_find_func_offset_from_file’ at elf.c:384:2:
+elf.c:57:9: warning: ‘elf_fd.elf’ may be used uninitialized [-Wmaybe-uninitialized]
+   57 |         elf_end(elf_fd->elf);
+      |         ^~~~~~~~~~~~~~~~~~~~
+elf.c: In function ‘elf_find_func_offset_from_file’:
+elf.c:377:23: note: ‘elf_fd.elf’ was declared here
+  377 |         struct elf_fd elf_fd;
+      |                       ^~~~~~
+In function ‘elf_close’,
+    inlined from ‘elf_close’ at elf.c:53:6,
+    inlined from ‘elf_find_func_offset_from_file’ at elf.c:384:2:
+elf.c:58:9: warning: ‘elf_fd.fd’ may be used uninitialized [-Wmaybe-uninitialized]
+   58 |         close(elf_fd->fd);
+      |         ^~~~~~~~~~~~~~~~~
+elf.c: In function ‘elf_find_func_offset_from_file’:
+elf.c:377:23: note: ‘elf_fd.fd’ was declared here
+  377 |         struct elf_fd elf_fd;
+      |                       ^~~~~~
+```
+
+In reality, our use is fine, it's just that GCC doesn't model errno
+here (see linked GCC bug). Suppress -Wmaybe-uninitialized accordingly
+by initializing elf_fd.fd to -1 and elf_fd.elf to NULL.
+
+I've done this in two other functions as well given it could easily
+occur there too (same access/use pattern).
+
+Signed-off-by: Sam James <sam@gentoo.org>
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://gcc.gnu.org/PR114952
+Link: https://lore.kernel.org/bpf/14ec488a1cac02794c2fa2b83ae0cef1bce2cb36.1723578546.git.sam@gentoo.org
+---
+ tools/lib/bpf/elf.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/tools/lib/bpf/elf.c
++++ b/tools/lib/bpf/elf.c
+@@ -16,6 +16,9 @@ int elf_open(const char *binary_path, st
+       int fd, ret;
+       Elf *elf;
++      elf_fd->elf = NULL;
++      elf_fd->fd = -1;
++
+       if (elf_version(EV_CURRENT) == EV_NONE) {
+               pr_warn("elf: failed to init libelf for %s\n", binary_path);
+               return -LIBBPF_ERRNO__LIBELF;
diff --git a/target/linux/generic/backport-6.6/300-v6.7-arm64-swiotlb-Reduce-the-default-size-if-no-ZONE_DMA.patch b/target/linux/generic/backport-6.6/300-v6.7-arm64-swiotlb-Reduce-the-default-size-if-no-ZONE_DMA.patch
new file mode 100644 (file)
index 0000000..6ede4d3
--- /dev/null
@@ -0,0 +1,51 @@
+From 65033574ade97afccba074d837fd269903a83a9a Mon Sep 17 00:00:00 2001
+From: Catalin Marinas <catalin.marinas@arm.com>
+Date: Thu, 5 Oct 2023 16:40:30 +0100
+Subject: [PATCH] arm64: swiotlb: Reduce the default size if no ZONE_DMA
+ bouncing needed
+
+With CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC enabled, the arm64 kernel still
+allocates the default SWIOTLB buffer (64MB) even if ZONE_DMA is disabled
+or all the RAM fits into this zone. However, this potentially wastes a
+non-negligible amount of memory on platforms with little RAM.
+
+Reduce the SWIOTLB size to 1MB per 1GB of RAM if only needed for
+kmalloc() buffer bouncing.
+
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+Suggested-by: Ross Burton <ross.burton@arm.com>
+Cc: Ross Burton <ross.burton@arm.com>
+Cc: Will Deacon <will@kernel.org>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+---
+ arch/arm64/mm/init.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -16,6 +16,7 @@
+ #include <linux/nodemask.h>
+ #include <linux/initrd.h>
+ #include <linux/gfp.h>
++#include <linux/math.h>
+ #include <linux/memblock.h>
+ #include <linux/sort.h>
+ #include <linux/of.h>
+@@ -493,8 +494,16 @@ void __init mem_init(void)
+ {
+       bool swiotlb = max_pfn > PFN_DOWN(arm64_dma_phys_limit);
+-      if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC))
++      if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) && !swiotlb) {
++              /*
++               * If no bouncing needed for ZONE_DMA, reduce the swiotlb
++               * buffer for kmalloc() bouncing to 1MB per 1GB of RAM.
++               */
++              unsigned long size =
++                      DIV_ROUND_UP(memblock_phys_mem_size(), 1024);
++              swiotlb_adjust_size(min(swiotlb_size_or_default(), size));
+               swiotlb = true;
++      }
+       swiotlb_init(swiotlb, SWIOTLB_VERBOSE);
diff --git a/target/linux/generic/backport-6.6/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch b/target/linux/generic/backport-6.6/301-v6.9-kernel.h-removed-REPEAT_BYTE-from-kernel.h.patch
new file mode 100644 (file)
index 0000000..adc924c
--- /dev/null
@@ -0,0 +1,161 @@
+From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001
+From: Tanzir Hasan <tanzirh@google.com>
+Date: Tue, 26 Dec 2023 18:00:00 +0000
+Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h
+
+This patch creates wordpart.h and includes it in asm/word-at-a-time.h
+for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h
+because of REPEAT_BYTE. Moving this to another header and including it
+where necessary allows us to not include the bloated kernel.h. Making
+this implicit dependency on REPEAT_BYTE explicit allows for later
+improvements in the lib/string.c inclusion list.
+
+Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
+Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Signed-off-by: Tanzir Hasan <tanzirh@google.com>
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com
+Signed-off-by: Kees Cook <keescook@chromium.org>
+---
+ arch/arm/include/asm/word-at-a-time.h     |  3 ++-
+ arch/arm64/include/asm/word-at-a-time.h   |  3 ++-
+ arch/powerpc/include/asm/word-at-a-time.h |  4 ++--
+ arch/riscv/include/asm/word-at-a-time.h   |  3 ++-
+ arch/s390/include/asm/word-at-a-time.h    |  3 ++-
+ arch/sh/include/asm/word-at-a-time.h      |  2 ++
+ arch/x86/include/asm/word-at-a-time.h     |  3 ++-
+ arch/x86/kvm/mmu/mmu.c                    |  1 +
+ fs/namei.c                                |  2 +-
+ include/asm-generic/word-at-a-time.h      |  3 ++-
+ include/linux/kernel.h                    |  8 --------
+ include/linux/wordpart.h                  | 13 +++++++++++++
+ 12 files changed, 31 insertions(+), 17 deletions(-)
+ create mode 100644 include/linux/wordpart.h
+
+--- a/arch/arm/include/asm/word-at-a-time.h
++++ b/arch/arm/include/asm/word-at-a-time.h
+@@ -8,7 +8,8 @@
+  * Little-endian word-at-a-time zero byte handling.
+  * Heavily based on the x86 algorithm.
+  */
+-#include <linux/kernel.h>
++#include <linux/bitops.h>
++#include <linux/wordpart.h>
+ struct word_at_a_time {
+       const unsigned long one_bits, high_bits;
+--- a/arch/arm64/include/asm/word-at-a-time.h
++++ b/arch/arm64/include/asm/word-at-a-time.h
+@@ -9,7 +9,8 @@
+ #ifndef __AARCH64EB__
+-#include <linux/kernel.h>
++#include <linux/bitops.h>
++#include <linux/wordpart.h>
+ struct word_at_a_time {
+       const unsigned long one_bits, high_bits;
+--- a/arch/powerpc/include/asm/word-at-a-time.h
++++ b/arch/powerpc/include/asm/word-at-a-time.h
+@@ -4,8 +4,8 @@
+ /*
+  * Word-at-a-time interfaces for PowerPC.
+  */
+-
+-#include <linux/kernel.h>
++#include <linux/bitops.h>
++#include <linux/wordpart.h>
+ #include <asm/asm-compat.h>
+ #include <asm/extable.h>
+--- a/arch/sh/include/asm/word-at-a-time.h
++++ b/arch/sh/include/asm/word-at-a-time.h
+@@ -5,6 +5,8 @@
+ #ifdef CONFIG_CPU_BIG_ENDIAN
+ # include <asm-generic/word-at-a-time.h>
+ #else
++#include <linux/bitops.h>
++#include <linux/wordpart.h>
+ /*
+  * Little-endian version cribbed from x86.
+  */
+--- a/arch/x86/include/asm/word-at-a-time.h
++++ b/arch/x86/include/asm/word-at-a-time.h
+@@ -2,7 +2,8 @@
+ #ifndef _ASM_WORD_AT_A_TIME_H
+ #define _ASM_WORD_AT_A_TIME_H
+-#include <linux/kernel.h>
++#include <linux/bitops.h>
++#include <linux/wordpart.h>
+ /*
+  * This is largely generic for little-endian machines, but the
+--- a/arch/x86/kvm/mmu/mmu.c
++++ b/arch/x86/kvm/mmu/mmu.c
+@@ -47,6 +47,7 @@
+ #include <linux/kern_levels.h>
+ #include <linux/kstrtox.h>
+ #include <linux/kthread.h>
++#include <linux/wordpart.h>
+ #include <asm/page.h>
+ #include <asm/memtype.h>
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -17,8 +17,8 @@
+ #include <linux/init.h>
+ #include <linux/export.h>
+-#include <linux/kernel.h>
+ #include <linux/slab.h>
++#include <linux/wordpart.h>
+ #include <linux/fs.h>
+ #include <linux/filelock.h>
+ #include <linux/namei.h>
+--- a/include/asm-generic/word-at-a-time.h
++++ b/include/asm-generic/word-at-a-time.h
+@@ -2,7 +2,8 @@
+ #ifndef _ASM_WORD_AT_A_TIME_H
+ #define _ASM_WORD_AT_A_TIME_H
+-#include <linux/kernel.h>
++#include <linux/bitops.h>
++#include <linux/wordpart.h>
+ #include <asm/byteorder.h>
+ #ifdef __BIG_ENDIAN
+--- a/include/linux/kernel.h
++++ b/include/linux/kernel.h
+@@ -38,14 +38,6 @@
+ #define STACK_MAGIC   0xdeadbeef
+-/**
+- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
+- * @x: value to repeat
+- *
+- * NOTE: @x is not checked for > 0xff; larger values produce odd results.
+- */
+-#define REPEAT_BYTE(x)        ((~0ul / 0xff) * (x))
+-
+ /* generic data direction definitions */
+ #define READ                  0
+ #define WRITE                 1
+--- /dev/null
++++ b/include/linux/wordpart.h
+@@ -0,0 +1,13 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++
++#ifndef _LINUX_WORDPART_H
++#define _LINUX_WORDPART_H
++/**
++ * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
++ * @x: value to repeat
++ *
++ * NOTE: @x is not checked for > 0xff; larger values produce odd results.
++ */
++#define REPEAT_BYTE(x)        ((~0ul / 0xff) * (x))
++
++#endif // _LINUX_WORDPART_H
diff --git a/target/linux/generic/backport-6.6/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch b/target/linux/generic/backport-6.6/302-v6.9-kernel.h-Move-upper_-_bits-and-lower_-_bits-to-wordp.patch
new file mode 100644 (file)
index 0000000..b9c40e6
--- /dev/null
@@ -0,0 +1,107 @@
+From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Date: Wed, 14 Feb 2024 19:26:32 +0200
+Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to
+ wordpart.h
+
+The wordpart.h header is collecting APIs related to the handling
+parts of the word (usually in byte granularity). The upper_*_bits()
+and lower_*_bits() are good candidates to be moved to there.
+
+This helps to clean up header dependency hell with regard to kernel.h
+as the latter gathers completely unrelated stuff together and slows
+down compilation (especially when it's included into other header).
+
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com
+Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
+Signed-off-by: Kees Cook <keescook@chromium.org>
+---
+ include/linux/kernel.h   | 30 ++----------------------------
+ include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++
+ 2 files changed, 31 insertions(+), 28 deletions(-)
+
+--- a/include/linux/kernel.h
++++ b/include/linux/kernel.h
+@@ -32,6 +32,8 @@
+ #include <linux/sprintf.h>
+ #include <linux/static_call_types.h>
+ #include <linux/instruction_pointer.h>
++#include <linux/wordpart.h>
++
+ #include <asm/byteorder.h>
+ #include <uapi/linux/kernel.h>
+@@ -57,34 +59,6 @@
+ }                                     \
+ )
+-/**
+- * upper_32_bits - return bits 32-63 of a number
+- * @n: the number we're accessing
+- *
+- * A basic shift-right of a 64- or 32-bit quantity.  Use this to suppress
+- * the "right shift count >= width of type" warning when that quantity is
+- * 32-bits.
+- */
+-#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
+-
+-/**
+- * lower_32_bits - return bits 0-31 of a number
+- * @n: the number we're accessing
+- */
+-#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
+-
+-/**
+- * upper_16_bits - return bits 16-31 of a number
+- * @n: the number we're accessing
+- */
+-#define upper_16_bits(n) ((u16)((n) >> 16))
+-
+-/**
+- * lower_16_bits - return bits 0-15 of a number
+- * @n: the number we're accessing
+- */
+-#define lower_16_bits(n) ((u16)((n) & 0xffff))
+-
+ struct completion;
+ struct user;
+--- a/include/linux/wordpart.h
++++ b/include/linux/wordpart.h
+@@ -2,6 +2,35 @@
+ #ifndef _LINUX_WORDPART_H
+ #define _LINUX_WORDPART_H
++
++/**
++ * upper_32_bits - return bits 32-63 of a number
++ * @n: the number we're accessing
++ *
++ * A basic shift-right of a 64- or 32-bit quantity.  Use this to suppress
++ * the "right shift count >= width of type" warning when that quantity is
++ * 32-bits.
++ */
++#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
++
++/**
++ * lower_32_bits - return bits 0-31 of a number
++ * @n: the number we're accessing
++ */
++#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
++
++/**
++ * upper_16_bits - return bits 16-31 of a number
++ * @n: the number we're accessing
++ */
++#define upper_16_bits(n) ((u16)((n) >> 16))
++
++/**
++ * lower_16_bits - return bits 0-15 of a number
++ * @n: the number we're accessing
++ */
++#define lower_16_bits(n) ((u16)((n) & 0xffff))
++
+ /**
+  * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
+  * @x: value to repeat
diff --git a/target/linux/generic/backport-6.6/310-v6.7-mips-kexec-fix-the-incorrect-ifdeffery-and-dependenc.patch b/target/linux/generic/backport-6.6/310-v6.7-mips-kexec-fix-the-incorrect-ifdeffery-and-dependenc.patch
new file mode 100644 (file)
index 0000000..6d3c7d0
--- /dev/null
@@ -0,0 +1,206 @@
+From 8cd2accb71f5eb8e92d775fc1978d3779875c2e5 Mon Sep 17 00:00:00 2001
+From: Baoquan He <bhe@redhat.com>
+Date: Fri, 8 Dec 2023 15:30:34 +0800
+Subject: [PATCH] mips, kexec: fix the incorrect ifdeffery and dependency of
+ CONFIG_KEXEC
+
+The select of KEXEC for CRASH_DUMP in kernel/Kconfig.kexec will be
+dropped, then compiling errors will be triggered if below config items are
+set:
+
+===
+CONFIG_CRASH_CORE=y
+CONFIG_KEXEC_CORE=y
+CONFIG_CRASH_DUMP=y
+===
+
+--------------------------------------------------------------------
+mipsel-linux-ld: kernel/kexec_core.o: in function `kimage_free':
+kernel/kexec_core.c:(.text+0x2200): undefined reference to `machine_kexec_cleanup'
+mipsel-linux-ld: kernel/kexec_core.o: in function `__crash_kexec':
+kernel/kexec_core.c:(.text+0x2480): undefined reference to `machine_crash_shutdown'
+mipsel-linux-ld: kernel/kexec_core.c:(.text+0x2488): undefined reference to `machine_kexec'
+mipsel-linux-ld: kernel/kexec_core.o: in function `kernel_kexec':
+kernel/kexec_core.c:(.text+0x29b8): undefined reference to `machine_shutdown'
+mipsel-linux-ld: kernel/kexec_core.c:(.text+0x29c0): undefined reference to `machine_kexec'
+--------------------------------------------------------------------
+
+Here, change the dependency of building kexec_core related object files,
+and the ifdeffery in mips from CONFIG_KEXEC to CONFIG_KEXEC_CORE.
+
+Link: https://lkml.kernel.org/r/20231208073036.7884-4-bhe@redhat.com
+Signed-off-by: Baoquan He <bhe@redhat.com>
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202311302042.sn8cDPIX-lkp@intel.com/
+Cc: Eric DeVolder <eric_devolder@yahoo.com>
+Cc: Ignat Korchagin <ignat@cloudflare.com>
+Cc: Stephen Rothwell <sfr@canb.auug.org.au>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+ arch/mips/cavium-octeon/smp.c   |  4 ++--
+ arch/mips/include/asm/kexec.h   |  2 +-
+ arch/mips/include/asm/smp-ops.h |  2 +-
+ arch/mips/include/asm/smp.h     |  2 +-
+ arch/mips/kernel/Makefile       |  2 +-
+ arch/mips/kernel/smp-bmips.c    |  4 ++--
+ arch/mips/kernel/smp-cps.c      | 10 +++++-----
+ arch/mips/loongson64/reset.c    |  4 ++--
+ arch/mips/loongson64/smp.c      |  2 +-
+ 9 files changed, 16 insertions(+), 16 deletions(-)
+
+--- a/arch/mips/cavium-octeon/smp.c
++++ b/arch/mips/cavium-octeon/smp.c
+@@ -422,7 +422,7 @@ static const struct plat_smp_ops octeon_
+       .cpu_disable            = octeon_cpu_disable,
+       .cpu_die                = octeon_cpu_die,
+ #endif
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+       .kexec_nonboot_cpu      = kexec_nonboot_cpu_jump,
+ #endif
+ };
+@@ -502,7 +502,7 @@ static const struct plat_smp_ops octeon_
+       .cpu_disable            = octeon_cpu_disable,
+       .cpu_die                = octeon_cpu_die,
+ #endif
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+       .kexec_nonboot_cpu      = kexec_nonboot_cpu_jump,
+ #endif
+ };
+--- a/arch/mips/include/asm/kexec.h
++++ b/arch/mips/include/asm/kexec.h
+@@ -31,7 +31,7 @@ static inline void crash_setup_regs(stru
+               prepare_frametrace(newregs);
+ }
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+ struct kimage;
+ extern unsigned long kexec_args[4];
+ extern int (*_machine_kexec_prepare)(struct kimage *);
+--- a/arch/mips/include/asm/smp-ops.h
++++ b/arch/mips/include/asm/smp-ops.h
+@@ -35,7 +35,7 @@ struct plat_smp_ops {
+       void (*cpu_die)(unsigned int cpu);
+       void (*cleanup_dead_cpu)(unsigned cpu);
+ #endif
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+       void (*kexec_nonboot_cpu)(void);
+ #endif
+ };
+--- a/arch/mips/include/asm/smp.h
++++ b/arch/mips/include/asm/smp.h
+@@ -93,7 +93,7 @@ static inline void __cpu_die(unsigned in
+ extern void __noreturn play_dead(void);
+ #endif
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+ static inline void kexec_nonboot_cpu(void)
+ {
+       extern const struct plat_smp_ops *mp_ops;       /* private */
+--- a/arch/mips/kernel/Makefile
++++ b/arch/mips/kernel/Makefile
+@@ -90,7 +90,7 @@ obj-$(CONFIG_GPIO_TXX9)              += gpio_txx9.o
+ obj-$(CONFIG_RELOCATABLE)     += relocate.o
+-obj-$(CONFIG_KEXEC)           += machine_kexec.o relocate_kernel.o crash.o
++obj-$(CONFIG_KEXEC_CORE)      += machine_kexec.o relocate_kernel.o crash.o
+ obj-$(CONFIG_CRASH_DUMP)      += crash_dump.o
+ obj-$(CONFIG_EARLY_PRINTK)    += early_printk.o
+ obj-$(CONFIG_EARLY_PRINTK_8250)       += early_printk_8250.o
+--- a/arch/mips/kernel/smp-bmips.c
++++ b/arch/mips/kernel/smp-bmips.c
+@@ -434,7 +434,7 @@ const struct plat_smp_ops bmips43xx_smp_
+       .cpu_disable            = bmips_cpu_disable,
+       .cpu_die                = bmips_cpu_die,
+ #endif
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+       .kexec_nonboot_cpu      = kexec_nonboot_cpu_jump,
+ #endif
+ };
+@@ -451,7 +451,7 @@ const struct plat_smp_ops bmips5000_smp_
+       .cpu_disable            = bmips_cpu_disable,
+       .cpu_die                = bmips_cpu_die,
+ #endif
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+       .kexec_nonboot_cpu      = kexec_nonboot_cpu_jump,
+ #endif
+ };
+--- a/arch/mips/kernel/smp-cps.c
++++ b/arch/mips/kernel/smp-cps.c
+@@ -395,7 +395,7 @@ static void cps_smp_finish(void)
+       local_irq_enable();
+ }
+-#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_KEXEC)
++#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_KEXEC_CORE)
+ enum cpu_death {
+       CPU_DEATH_HALT,
+@@ -432,7 +432,7 @@ static void cps_shutdown_this_cpu(enum c
+       }
+ }
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+ static void cps_kexec_nonboot_cpu(void)
+ {
+@@ -442,9 +442,9 @@ static void cps_kexec_nonboot_cpu(void)
+               cps_shutdown_this_cpu(CPU_DEATH_POWER);
+ }
+-#endif /* CONFIG_KEXEC */
++#endif /* CONFIG_KEXEC_CORE */
+-#endif /* CONFIG_HOTPLUG_CPU || CONFIG_KEXEC */
++#endif /* CONFIG_HOTPLUG_CPU || CONFIG_KEXEC_CORE */
+ #ifdef CONFIG_HOTPLUG_CPU
+@@ -613,7 +613,7 @@ static const struct plat_smp_ops cps_smp
+       .cpu_die                = cps_cpu_die,
+       .cleanup_dead_cpu       = cps_cleanup_dead_cpu,
+ #endif
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+       .kexec_nonboot_cpu      = cps_kexec_nonboot_cpu,
+ #endif
+ };
+--- a/arch/mips/loongson64/reset.c
++++ b/arch/mips/loongson64/reset.c
+@@ -39,7 +39,7 @@ static int firmware_poweroff(struct sys_
+       return NOTIFY_DONE;
+ }
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+ /* 0X80000000~0X80200000 is safe */
+ #define MAX_ARGS      64
+@@ -152,7 +152,7 @@ static int __init mips_reboot_setup(void
+                                firmware_poweroff, NULL);
+       }
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+       kexec_argv = kmalloc(KEXEC_ARGV_SIZE, GFP_KERNEL);
+       if (WARN_ON(!kexec_argv))
+               return -ENOMEM;
+--- a/arch/mips/loongson64/smp.c
++++ b/arch/mips/loongson64/smp.c
+@@ -883,7 +883,7 @@ const struct plat_smp_ops loongson3_smp_
+       .cpu_disable = loongson3_cpu_disable,
+       .cpu_die = loongson3_cpu_die,
+ #endif
+-#ifdef CONFIG_KEXEC
++#ifdef CONFIG_KEXEC_CORE
+       .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
+ #endif
+ };
diff --git a/target/linux/generic/backport-6.6/320-v6.11-mips-bmips-rework-and-cache-CBR-addr-handling.patch b/target/linux/generic/backport-6.6/320-v6.11-mips-bmips-rework-and-cache-CBR-addr-handling.patch
new file mode 100644 (file)
index 0000000..21eaa3d
--- /dev/null
@@ -0,0 +1,171 @@
+From a5c05453a13ab324ad8719e8a23dfb6af01f3652 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 20 Jun 2024 17:26:42 +0200
+Subject: [PATCH 1/4] mips: bmips: rework and cache CBR addr handling
+
+Rework the handling of the CBR address and cache it. This address
+doesn't change and can be cached instead of reading the register every
+time.
+
+This is in preparation of permitting to tweak the CBR address in DT with
+broken SoC or bootloader.
+
+bmips_cbr_addr is defined in setup.c for each arch to keep compatibility
+with legacy brcm47xx/brcm63xx and generic BMIPS target.
+
+Acked-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ arch/mips/bcm47xx/prom.c      | 3 +++
+ arch/mips/bcm47xx/setup.c     | 4 ++++
+ arch/mips/bcm63xx/prom.c      | 3 +++
+ arch/mips/bcm63xx/setup.c     | 4 ++++
+ arch/mips/bmips/dma.c         | 2 +-
+ arch/mips/bmips/setup.c       | 7 ++++++-
+ arch/mips/include/asm/bmips.h | 1 +
+ arch/mips/kernel/smp-bmips.c  | 4 ++--
+ 8 files changed, 24 insertions(+), 4 deletions(-)
+
+--- a/arch/mips/bcm47xx/prom.c
++++ b/arch/mips/bcm47xx/prom.c
+@@ -32,6 +32,7 @@
+ #include <linux/ssb/ssb_driver_chipcommon.h>
+ #include <linux/ssb/ssb_regs.h>
+ #include <linux/smp.h>
++#include <asm/bmips.h>
+ #include <asm/bootinfo.h>
+ #include <bcm47xx.h>
+ #include <bcm47xx_board.h>
+@@ -109,6 +110,8 @@ static __init void prom_init_mem(void)
+ void __init prom_init(void)
+ {
++      /* Cache CBR addr before CPU/DMA setup */
++      bmips_cbr_addr = BMIPS_GET_CBR();
+       prom_init_mem();
+       setup_8250_early_printk_port(CKSEG1ADDR(BCM47XX_SERIAL_ADDR), 0, 0);
+ }
+--- a/arch/mips/bcm47xx/setup.c
++++ b/arch/mips/bcm47xx/setup.c
+@@ -37,6 +37,7 @@
+ #include <linux/ssb/ssb.h>
+ #include <linux/ssb/ssb_embedded.h>
+ #include <linux/bcma/bcma_soc.h>
++#include <asm/bmips.h>
+ #include <asm/bootinfo.h>
+ #include <asm/idle.h>
+ #include <asm/prom.h>
+@@ -45,6 +46,9 @@
+ #include <bcm47xx.h>
+ #include <bcm47xx_board.h>
++/* CBR addr doesn't change and we can cache it */
++void __iomem *bmips_cbr_addr __read_mostly;
++
+ union bcm47xx_bus bcm47xx_bus;
+ EXPORT_SYMBOL(bcm47xx_bus);
+--- a/arch/mips/bcm63xx/prom.c
++++ b/arch/mips/bcm63xx/prom.c
+@@ -22,6 +22,9 @@ void __init prom_init(void)
+ {
+       u32 reg, mask;
++      /* Cache CBR addr before CPU/DMA setup */
++      bmips_cbr_addr = BMIPS_GET_CBR();
++
+       bcm63xx_cpu_init();
+       /* stop any running watchdog */
+--- a/arch/mips/bcm63xx/setup.c
++++ b/arch/mips/bcm63xx/setup.c
+@@ -12,6 +12,7 @@
+ #include <linux/memblock.h>
+ #include <linux/ioport.h>
+ #include <linux/pm.h>
++#include <asm/bmips.h>
+ #include <asm/bootinfo.h>
+ #include <asm/time.h>
+ #include <asm/reboot.h>
+@@ -22,6 +23,9 @@
+ #include <bcm63xx_io.h>
+ #include <bcm63xx_gpio.h>
++/* CBR addr doesn't change and we can cache it */
++void __iomem *bmips_cbr_addr __read_mostly;
++
+ void bcm63xx_machine_halt(void)
+ {
+       pr_info("System halted\n");
+--- a/arch/mips/bmips/dma.c
++++ b/arch/mips/bmips/dma.c
+@@ -9,7 +9,7 @@ bool bmips_rac_flush_disable;
+ void arch_sync_dma_for_cpu_all(void)
+ {
+-      void __iomem *cbr = BMIPS_GET_CBR();
++      void __iomem *cbr = bmips_cbr_addr;
+       u32 cfg;
+       if (boot_cpu_type() != CPU_BMIPS3300 &&
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -34,6 +34,9 @@
+ #define REG_BCM6328_OTP               ((void __iomem *)CKSEG1ADDR(0x1000062c))
+ #define BCM6328_TP1_DISABLED  BIT(9)
++/* CBR addr doesn't change and we can cache it */
++void __iomem *bmips_cbr_addr __read_mostly;
++
+ extern bool bmips_rac_flush_disable;
+ static const unsigned long kbase = VMLINUX_LOAD_ADDRESS & 0xfff00000;
+@@ -111,7 +114,7 @@ static void bcm6358_quirks(void)
+        * because the bootloader is not initializing it properly.
+        */
+       bmips_rac_flush_disable = !!(read_c0_brcm_cmt_local() & (1 << 31)) ||
+-                                !!BMIPS_GET_CBR();
++                                !!bmips_cbr_addr;
+ }
+ static void bcm6368_quirks(void)
+@@ -144,6 +147,8 @@ static void __init bmips_init_cfe(void)
+ void __init prom_init(void)
+ {
++      /* Cache CBR addr before CPU/DMA setup */
++      bmips_cbr_addr = BMIPS_GET_CBR();
+       bmips_init_cfe();
+       bmips_cpu_setup();
+       register_bmips_smp_ops();
+--- a/arch/mips/include/asm/bmips.h
++++ b/arch/mips/include/asm/bmips.h
+@@ -81,6 +81,7 @@ extern char bmips_smp_movevec[];
+ extern char bmips_smp_int_vec[];
+ extern char bmips_smp_int_vec_end[];
++extern void __iomem *bmips_cbr_addr;
+ extern int bmips_smp_enabled;
+ extern int bmips_cpu_offset;
+ extern cpumask_t bmips_booted_mask;
+--- a/arch/mips/kernel/smp-bmips.c
++++ b/arch/mips/kernel/smp-bmips.c
+@@ -518,7 +518,7 @@ static void bmips_set_reset_vec(int cpu,
+               info.val = val;
+               bmips_set_reset_vec_remote(&info);
+       } else {
+-              void __iomem *cbr = BMIPS_GET_CBR();
++              void __iomem *cbr = bmips_cbr_addr;
+               if (cpu == 0)
+                       __raw_writel(val, cbr + BMIPS_RELO_VECTOR_CONTROL_0);
+@@ -591,7 +591,7 @@ asmlinkage void __weak plat_wired_tlb_se
+ void bmips_cpu_setup(void)
+ {
+-      void __iomem __maybe_unused *cbr = BMIPS_GET_CBR();
++      void __iomem __maybe_unused *cbr = bmips_cbr_addr;
+       u32 __maybe_unused cfg;
+       switch (current_cpu_type()) {
diff --git a/target/linux/generic/backport-6.6/321-v6.11-mips-bmips-setup-make-CBR-address-configurable.patch b/target/linux/generic/backport-6.6/321-v6.11-mips-bmips-setup-make-CBR-address-configurable.patch
new file mode 100644 (file)
index 0000000..10a710a
--- /dev/null
@@ -0,0 +1,111 @@
+From b95b30e50aed225d26e20737873ae2404941901c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 20 Jun 2024 17:26:44 +0200
+Subject: [PATCH 3/4] mips: bmips: setup: make CBR address configurable
+
+Add support to provide CBR address from DT to handle broken
+SoC/Bootloader that doesn't correctly init it. This permits to use the
+RAC flush even in these condition.
+
+To provide a CBR address from DT, the property "brcm,bmips-cbr-reg"
+needs to be set in the "cpus" node. On DT init, this property presence
+will be checked and will set the bmips_cbr_addr value accordingly. Also
+bmips_rac_flush_disable will be set to false as RAC flush can be
+correctly supported.
+
+The CBR address from DT will overwrite the cached one and the
+one set in the CBR register will be ignored.
+
+Also the DT CBR address is validated on being outside DRAM window.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Acked-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ arch/mips/bcm47xx/setup.c |  6 +++++-
+ arch/mips/bcm63xx/setup.c |  6 +++++-
+ arch/mips/bmips/setup.c   | 30 ++++++++++++++++++++++++++++--
+ 3 files changed, 38 insertions(+), 4 deletions(-)
+
+--- a/arch/mips/bcm47xx/setup.c
++++ b/arch/mips/bcm47xx/setup.c
+@@ -46,7 +46,11 @@
+ #include <bcm47xx.h>
+ #include <bcm47xx_board.h>
+-/* CBR addr doesn't change and we can cache it */
++/*
++ * CBR addr doesn't change and we can cache it.
++ * For broken SoC/Bootloader CBR addr might also be provided via DT
++ * with "brcm,bmips-cbr-reg" in the "cpus" node.
++ */
+ void __iomem *bmips_cbr_addr __read_mostly;
+ union bcm47xx_bus bcm47xx_bus;
+--- a/arch/mips/bcm63xx/setup.c
++++ b/arch/mips/bcm63xx/setup.c
+@@ -23,7 +23,11 @@
+ #include <bcm63xx_io.h>
+ #include <bcm63xx_gpio.h>
+-/* CBR addr doesn't change and we can cache it */
++/*
++ * CBR addr doesn't change and we can cache it.
++ * For broken SoC/Bootloader CBR addr might also be provided via DT
++ * with "brcm,bmips-cbr-reg" in the "cpus" node.
++ */
+ void __iomem *bmips_cbr_addr __read_mostly;
+ void bcm63xx_machine_halt(void)
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -34,7 +34,11 @@
+ #define REG_BCM6328_OTP               ((void __iomem *)CKSEG1ADDR(0x1000062c))
+ #define BCM6328_TP1_DISABLED  BIT(9)
+-/* CBR addr doesn't change and we can cache it */
++/*
++ * CBR addr doesn't change and we can cache it.
++ * For broken SoC/Bootloader CBR addr might also be provided via DT
++ * with "brcm,bmips-cbr-reg" in the "cpus" node.
++ */
+ void __iomem *bmips_cbr_addr __read_mostly;
+ extern bool bmips_rac_flush_disable;
+@@ -208,13 +212,35 @@ void __init plat_mem_setup(void)
+ void __init device_tree_init(void)
+ {
+       struct device_node *np;
++      u32 addr;
+       unflatten_and_copy_device_tree();
+       /* Disable SMP boot unless both CPUs are listed in DT and !disabled */
+       np = of_find_node_by_name(NULL, "cpus");
+-      if (np && of_get_available_child_count(np) <= 1)
++      if (!np)
++              return;
++
++      if (of_get_available_child_count(np) <= 1)
+               bmips_smp_enabled = 0;
++
++      /* Check if DT provide a CBR address */
++      if (of_property_read_u32(np, "brcm,bmips-cbr-reg", &addr))
++              goto exit;
++
++      /* Make sure CBR address is outside DRAM window */
++      if (addr >= (u32)memblock_start_of_DRAM() &&
++          addr < (u32)memblock_end_of_DRAM()) {
++              WARN(1, "DT CBR %x inside DRAM window. Ignoring DT CBR.\n",
++                   addr);
++              goto exit;
++      }
++
++      bmips_cbr_addr = (void __iomem *)addr;
++      /* Since CBR is provided by DT, enable RAC flush */
++      bmips_rac_flush_disable = false;
++
++exit:
+       of_node_put(np);
+ }
diff --git a/target/linux/generic/backport-6.6/322-v6.11-mips-bmips-enable-RAC-on-BMIPS4350.patch b/target/linux/generic/backport-6.6/322-v6.11-mips-bmips-enable-RAC-on-BMIPS4350.patch
new file mode 100644 (file)
index 0000000..2af45df
--- /dev/null
@@ -0,0 +1,57 @@
+From 04f38d1a4db017f17e82442727b91ce03dd72759 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20Cabanelas?= <dgcbueu@gmail.com>
+Date: Thu, 20 Jun 2024 17:26:45 +0200
+Subject: [PATCH 4/4] mips: bmips: enable RAC on BMIPS4350
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The data RAC is left disabled by the bootloader in some SoCs, at least in
+the core it boots from.
+Enabling this feature increases the performance up to +30% depending on the
+task.
+
+Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+[ rework code and reduce code duplication ]
+Acked-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+---
+ arch/mips/kernel/smp-bmips.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/arch/mips/kernel/smp-bmips.c
++++ b/arch/mips/kernel/smp-bmips.c
+@@ -592,6 +592,7 @@ asmlinkage void __weak plat_wired_tlb_se
+ void bmips_cpu_setup(void)
+ {
+       void __iomem __maybe_unused *cbr = bmips_cbr_addr;
++      u32 __maybe_unused rac_addr;
+       u32 __maybe_unused cfg;
+       switch (current_cpu_type()) {
+@@ -620,6 +621,23 @@ void bmips_cpu_setup(void)
+               __raw_readl(cbr + BMIPS_RAC_ADDRESS_RANGE);
+               break;
++      case CPU_BMIPS4350:
++              rac_addr = BMIPS_RAC_CONFIG_1;
++
++              if (!(read_c0_brcm_cmt_local() & (1 << 31)))
++                      rac_addr = BMIPS_RAC_CONFIG;
++
++              /* Enable data RAC */
++              cfg = __raw_readl(cbr + rac_addr);
++              __raw_writel(cfg | 0xf, cbr + rac_addr);
++              __raw_readl(cbr + rac_addr);
++
++              /* Flush stale data out of the readahead cache */
++              cfg = __raw_readl(cbr + BMIPS_RAC_CONFIG);
++              __raw_writel(cfg | 0x100, cbr + BMIPS_RAC_CONFIG);
++              __raw_readl(cbr + BMIPS_RAC_CONFIG);
++              break;
++
+       case CPU_BMIPS4380:
+               /* CBG workaround for early BMIPS4380 CPUs */
+               switch (read_c0_prid()) {
diff --git a/target/linux/generic/backport-6.6/400-v6.9-mtd-rawnand-brcmnand-Support-write-protection-settin.patch b/target/linux/generic/backport-6.6/400-v6.9-mtd-rawnand-brcmnand-Support-write-protection-settin.patch
new file mode 100644 (file)
index 0000000..2576241
--- /dev/null
@@ -0,0 +1,36 @@
+From 8e7daa85641c9559c113f6b217bdc923397de77c Mon Sep 17 00:00:00 2001
+From: William Zhang <william.zhang@broadcom.com>
+Date: Thu, 22 Feb 2024 19:47:58 -0800
+Subject: [PATCH] mtd: rawnand: brcmnand: Support write protection setting from
+ dts
+
+The write protection feature is controlled by the module parameter wp_on
+with default set to enabled. But not all the board use this feature
+especially in BCMBCA broadband board. And module parameter is not
+sufficient as different board can have different option.  Add a device
+tree property and allow this feature to be configured through the board
+dts on per board basis.
+
+Signed-off-by: William Zhang <william.zhang@broadcom.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Reviewed-by: Kamal Dasu <kamal.dasu@broadcom.com>
+Reviewed-by: David Regan <dregan@broadcom.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20240223034758.13753-14-william.zhang@broadcom.com
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -3189,6 +3189,10 @@ int brcmnand_probe(struct platform_devic
+       /* Disable XOR addressing */
+       brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
++      /* Check if the board connects the WP pin */
++      if (of_property_read_bool(dn, "brcm,wp-not-connected"))
++              wp_on = 0;
++
+       if (ctrl->features & BRCMNAND_HAS_WP) {
+               /* Permanently disable write protection */
+               if (wp_on == 2)
diff --git a/target/linux/generic/backport-6.6/401-v6.9-dt-bindings-mtd-add-basic-bindings-for-UBI.patch b/target/linux/generic/backport-6.6/401-v6.9-dt-bindings-mtd-add-basic-bindings-for-UBI.patch
new file mode 100644 (file)
index 0000000..8119948
--- /dev/null
@@ -0,0 +1,123 @@
+From 25d88bfd35bac3196eafa666e3b05033b46ffa21 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:32:00 +0000
+Subject: [PATCH 1/8] dt-bindings: mtd: add basic bindings for UBI
+
+Add basic bindings for UBI devices and volumes.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ .../bindings/mtd/partitions/linux,ubi.yaml    | 65 +++++++++++++++++++
+ .../bindings/mtd/partitions/ubi-volume.yaml   | 35 ++++++++++
+ 2 files changed, 100 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
+ create mode 100644 Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
+@@ -0,0 +1,65 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/partitions/linux,ubi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Unsorted Block Images
++
++description: |
++  UBI ("Unsorted Block Images") is a volume management system for raw
++  flash devices which manages multiple logical volumes on a single
++  physical flash device and spreads the I/O load (i.e wear-leveling)
++  across the whole flash chip.
++
++maintainers:
++  - Daniel Golle <daniel@makrotopia.org>
++
++allOf:
++  - $ref: partition.yaml#
++
++properties:
++  compatible:
++    const: linux,ubi
++
++  volumes:
++    type: object
++    description: UBI Volumes
++
++    patternProperties:
++      "^ubi-volume-.*$":
++        $ref: /schemas/mtd/partitions/ubi-volume.yaml#
++
++    unevaluatedProperties: false
++
++required:
++  - compatible
++
++unevaluatedProperties: false
++
++examples:
++  - |
++    partitions {
++        compatible = "fixed-partitions";
++        #address-cells = <1>;
++        #size-cells = <1>;
++
++        partition@0 {
++            reg = <0x0 0x100000>;
++            label = "bootloader";
++            read-only;
++        };
++
++        partition@100000 {
++            reg = <0x100000 0x1ff00000>;
++            label = "ubi";
++            compatible = "linux,ubi";
++
++            volumes {
++                ubi-volume-caldata {
++                    volid = <2>;
++                    volname = "rf";
++                };
++            };
++        };
++    };
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
+@@ -0,0 +1,35 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mtd/partitions/ubi-volume.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: UBI volume
++
++description: |
++  This binding describes a single UBI volume. Volumes can be matches either
++  by their ID or their name, or both.
++
++maintainers:
++  - Daniel Golle <daniel@makrotopia.org>
++
++properties:
++  volid:
++    $ref: /schemas/types.yaml#/definitions/uint32
++    description:
++      Match UBI volume ID
++
++  volname:
++    $ref: /schemas/types.yaml#/definitions/string
++    description:
++      Match UBI volume ID
++
++anyOf:
++  - required:
++      - volid
++
++  - required:
++      - volname
++
++# This is a generic file other binding inherit from and extend
++additionalProperties: true
diff --git a/target/linux/generic/backport-6.6/402-v6.9-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch b/target/linux/generic/backport-6.6/402-v6.9-dt-bindings-mtd-ubi-volume-allow-UBI-volumes-to-prov.patch
new file mode 100644 (file)
index 0000000..a394338
--- /dev/null
@@ -0,0 +1,50 @@
+From 95b113222b5164ac0887eb5c514ff3970a0136f0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:32:11 +0000
+Subject: [PATCH 2/8] dt-bindings: mtd: ubi-volume: allow UBI volumes to
+ provide NVMEM
+
+UBI volumes may be used to contain NVMEM bits, typically device MAC
+addresses or wireless radio calibration data.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ .../devicetree/bindings/mtd/partitions/linux,ubi.yaml  | 10 ++++++++++
+ .../devicetree/bindings/mtd/partitions/ubi-volume.yaml |  5 +++++
+ 2 files changed, 15 insertions(+)
+
+--- a/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
++++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
+@@ -59,6 +59,16 @@ examples:
+                 ubi-volume-caldata {
+                     volid = <2>;
+                     volname = "rf";
++
++                    nvmem-layout {
++                        compatible = "fixed-layout";
++                        #address-cells = <1>;
++                        #size-cells = <1>;
++
++                        eeprom@0 {
++                            reg = <0x0 0x1000>;
++                        };
++                    };
+                 };
+             };
+         };
+--- a/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
++++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
+@@ -24,6 +24,11 @@ properties:
+     description:
+       Match UBI volume ID
++  nvmem-layout:
++    $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
++    description:
++      This container may reference an NVMEM layout parser.
++
+ anyOf:
+   - required:
+       - volid
diff --git a/target/linux/generic/backport-6.6/403-v6.9-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch b/target/linux/generic/backport-6.6/403-v6.9-mtd-ubi-block-use-notifier-to-create-ubiblock-from-p.patch
new file mode 100644 (file)
index 0000000..f89330c
--- /dev/null
@@ -0,0 +1,285 @@
+From 2bba1cdcfcd2907d0696cc0139f1bd078d36ee81 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:32:35 +0000
+Subject: [PATCH 3/8] mtd: ubi: block: use notifier to create ubiblock from
+ parameter
+
+Use UBI_VOLUME_ADDED notification to create ubiblock device specified
+on kernel cmdline or module parameter.
+This makes thing more simple and has the advantage that ubiblock devices
+on volumes which are not present at the time the ubi module is probed
+will still be created.
+
+Suggested-by: Zhihao Cheng <chengzhihao1@huawei.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/block.c | 136 ++++++++++++++++++++--------------------
+ drivers/mtd/ubi/kapi.c  |  54 +++++++++++-----
+ drivers/mtd/ubi/ubi.h   |   1 +
+ 3 files changed, 106 insertions(+), 85 deletions(-)
+
+--- a/drivers/mtd/ubi/block.c
++++ b/drivers/mtd/ubi/block.c
+@@ -65,10 +65,10 @@ struct ubiblock_pdu {
+ };
+ /* Numbers of elements set in the @ubiblock_param array */
+-static int ubiblock_devs __initdata;
++static int ubiblock_devs;
+ /* MTD devices specification parameters */
+-static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata;
++static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES];
+ struct ubiblock {
+       struct ubi_volume_desc *desc;
+@@ -532,6 +532,70 @@ static int ubiblock_resize(struct ubi_vo
+       return 0;
+ }
++static bool
++match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id)
++{
++      int err, len, cur_ubi_num, cur_vol_id;
++
++      if (ubi_num == -1) {
++              /* No ubi num, name must be a vol device path */
++              err = ubi_get_num_by_path(name, &cur_ubi_num, &cur_vol_id);
++              if (err || vi->ubi_num != cur_ubi_num || vi->vol_id != cur_vol_id)
++                      return false;
++
++              return true;
++      }
++
++      if (vol_id == -1) {
++              /* Got ubi_num, but no vol_id, name must be volume name */
++              if (vi->ubi_num != ubi_num)
++                      return false;
++
++              len = strnlen(name, UBI_VOL_NAME_MAX + 1);
++              if (len < 1 || vi->name_len != len)
++                      return false;
++
++              if (strcmp(name, vi->name))
++                      return false;
++
++              return true;
++      }
++
++      if (vi->ubi_num != ubi_num)
++              return false;
++
++      if (vi->vol_id != vol_id)
++              return false;
++
++      return true;
++}
++
++static void
++ubiblock_create_from_param(struct ubi_volume_info *vi)
++{
++      int i, ret = 0;
++      struct ubiblock_param *p;
++
++      /*
++       * Iterate over ubiblock cmdline parameters. If a parameter matches the
++       * newly added volume create the ubiblock device for it.
++       */
++      for (i = 0; i < ubiblock_devs; i++) {
++              p = &ubiblock_param[i];
++
++              if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id))
++                      continue;
++
++              ret = ubiblock_create(vi);
++              if (ret) {
++                      pr_err(
++                             "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
++                             vi->name, p->ubi_num, p->vol_id, ret);
++              }
++              break;
++      }
++}
++
+ static int ubiblock_notify(struct notifier_block *nb,
+                        unsigned long notification_type, void *ns_ptr)
+ {
+@@ -539,10 +603,7 @@ static int ubiblock_notify(struct notifi
+       switch (notification_type) {
+       case UBI_VOLUME_ADDED:
+-              /*
+-               * We want to enforce explicit block device creation for
+-               * volumes, so when a volume is added we do nothing.
+-               */
++              ubiblock_create_from_param(&nt->vi);
+               break;
+       case UBI_VOLUME_REMOVED:
+               ubiblock_remove(&nt->vi);
+@@ -568,56 +629,6 @@ static struct notifier_block ubiblock_no
+       .notifier_call = ubiblock_notify,
+ };
+-static struct ubi_volume_desc * __init
+-open_volume_desc(const char *name, int ubi_num, int vol_id)
+-{
+-      if (ubi_num == -1)
+-              /* No ubi num, name must be a vol device path */
+-              return ubi_open_volume_path(name, UBI_READONLY);
+-      else if (vol_id == -1)
+-              /* No vol_id, must be vol_name */
+-              return ubi_open_volume_nm(ubi_num, name, UBI_READONLY);
+-      else
+-              return ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
+-}
+-
+-static void __init ubiblock_create_from_param(void)
+-{
+-      int i, ret = 0;
+-      struct ubiblock_param *p;
+-      struct ubi_volume_desc *desc;
+-      struct ubi_volume_info vi;
+-
+-      /*
+-       * If there is an error creating one of the ubiblocks, continue on to
+-       * create the following ubiblocks. This helps in a circumstance where
+-       * the kernel command-line specifies multiple block devices and some
+-       * may be broken, but we still want the working ones to come up.
+-       */
+-      for (i = 0; i < ubiblock_devs; i++) {
+-              p = &ubiblock_param[i];
+-
+-              desc = open_volume_desc(p->name, p->ubi_num, p->vol_id);
+-              if (IS_ERR(desc)) {
+-                      pr_err(
+-                             "UBI: block: can't open volume on ubi%d_%d, err=%ld\n",
+-                             p->ubi_num, p->vol_id, PTR_ERR(desc));
+-                      continue;
+-              }
+-
+-              ubi_get_volume_info(desc, &vi);
+-              ubi_close_volume(desc);
+-
+-              ret = ubiblock_create(&vi);
+-              if (ret) {
+-                      pr_err(
+-                             "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
+-                             vi.name, p->ubi_num, p->vol_id, ret);
+-                      continue;
+-              }
+-      }
+-}
+-
+ static void ubiblock_remove_all(void)
+ {
+       struct ubiblock *next;
+@@ -643,18 +654,7 @@ int __init ubiblock_init(void)
+       if (ubiblock_major < 0)
+               return ubiblock_major;
+-      /*
+-       * Attach block devices from 'block=' module param.
+-       * Even if one block device in the param list fails to come up,
+-       * still allow the module to load and leave any others up.
+-       */
+-      ubiblock_create_from_param();
+-
+-      /*
+-       * Block devices are only created upon user requests, so we ignore
+-       * existing volumes.
+-       */
+-      ret = ubi_register_volume_notifier(&ubiblock_notifier, 1);
++      ret = ubi_register_volume_notifier(&ubiblock_notifier, 0);
+       if (ret)
+               goto err_unreg;
+       return 0;
+--- a/drivers/mtd/ubi/kapi.c
++++ b/drivers/mtd/ubi/kapi.c
+@@ -280,6 +280,41 @@ struct ubi_volume_desc *ubi_open_volume_
+ EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
+ /**
++ * ubi_get_num_by_path - get UBI device and volume number from device path
++ * @pathname: volume character device node path
++ * @ubi_num: pointer to UBI device number to be set
++ * @vol_id: pointer to UBI volume ID to be set
++ *
++ * Returns 0 on success and sets ubi_num and vol_id, returns error otherwise.
++ */
++int ubi_get_num_by_path(const char *pathname, int *ubi_num, int *vol_id)
++{
++      int error;
++      struct path path;
++      struct kstat stat;
++
++      error = kern_path(pathname, LOOKUP_FOLLOW, &path);
++      if (error)
++              return error;
++
++      error = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
++      path_put(&path);
++      if (error)
++              return error;
++
++      if (!S_ISCHR(stat.mode))
++              return -EINVAL;
++
++      *ubi_num = ubi_major2num(MAJOR(stat.rdev));
++      *vol_id = MINOR(stat.rdev) - 1;
++
++      if (*vol_id < 0 || *ubi_num < 0)
++              return -ENODEV;
++
++      return 0;
++}
++
++/**
+  * ubi_open_volume_path - open UBI volume by its character device node path.
+  * @pathname: volume character device node path
+  * @mode: open mode
+@@ -290,32 +325,17 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
+ struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode)
+ {
+       int error, ubi_num, vol_id;
+-      struct path path;
+-      struct kstat stat;
+       dbg_gen("open volume %s, mode %d", pathname, mode);
+       if (!pathname || !*pathname)
+               return ERR_PTR(-EINVAL);
+-      error = kern_path(pathname, LOOKUP_FOLLOW, &path);
+-      if (error)
+-              return ERR_PTR(error);
+-
+-      error = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
+-      path_put(&path);
++      error = ubi_get_num_by_path(pathname, &ubi_num, &vol_id);
+       if (error)
+               return ERR_PTR(error);
+-      if (!S_ISCHR(stat.mode))
+-              return ERR_PTR(-EINVAL);
+-
+-      ubi_num = ubi_major2num(MAJOR(stat.rdev));
+-      vol_id = MINOR(stat.rdev) - 1;
+-
+-      if (vol_id >= 0 && ubi_num >= 0)
+-              return ubi_open_volume(ubi_num, vol_id, mode);
+-      return ERR_PTR(-ENODEV);
++      return ubi_open_volume(ubi_num, vol_id, mode);
+ }
+ EXPORT_SYMBOL_GPL(ubi_open_volume_path);
+--- a/drivers/mtd/ubi/ubi.h
++++ b/drivers/mtd/ubi/ubi.h
+@@ -956,6 +956,7 @@ void ubi_free_internal_volumes(struct ub
+ void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di);
+ void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol,
+                           struct ubi_volume_info *vi);
++int ubi_get_num_by_path(const char *pathname, int *ubi_num, int *vol_id);
+ /* scan.c */
+ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
+                     int pnum, const struct ubi_vid_hdr *vid_hdr);
diff --git a/target/linux/generic/backport-6.6/404-v6.9-mtd-ubi-attach-from-device-tree.patch b/target/linux/generic/backport-6.6/404-v6.9-mtd-ubi-attach-from-device-tree.patch
new file mode 100644 (file)
index 0000000..ec7b6c3
--- /dev/null
@@ -0,0 +1,205 @@
+From 6e331888643887ce85657527bc03f97d46235e71 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:33:14 +0000
+Subject: [PATCH 4/8] mtd: ubi: attach from device tree
+
+Introduce device tree compatible 'linux,ubi' and attach compatible MTD
+devices using the MTD add notifier. This is needed for a UBI device to
+be available early at boot (and not only after late_initcall), so
+volumes on them can be used eg. as NVMEM providers for other drivers.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/build.c | 135 ++++++++++++++++++++++++++++------------
+ 1 file changed, 96 insertions(+), 39 deletions(-)
+
+--- a/drivers/mtd/ubi/build.c
++++ b/drivers/mtd/ubi/build.c
+@@ -27,6 +27,7 @@
+ #include <linux/log2.h>
+ #include <linux/kthread.h>
+ #include <linux/kernel.h>
++#include <linux/of.h>
+ #include <linux/slab.h>
+ #include <linux/major.h>
+ #include "ubi.h"
+@@ -1214,43 +1215,43 @@ static struct mtd_info * __init open_mtd
+       return mtd;
+ }
+-static int __init ubi_init(void)
++static void ubi_notify_add(struct mtd_info *mtd)
+ {
+-      int err, i, k;
++      struct device_node *np = mtd_get_of_node(mtd);
++      int err;
+-      /* Ensure that EC and VID headers have correct size */
+-      BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
+-      BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
++      if (!of_device_is_compatible(np, "linux,ubi"))
++              return;
+-      if (mtd_devs > UBI_MAX_DEVICES) {
+-              pr_err("UBI error: too many MTD devices, maximum is %d\n",
+-                     UBI_MAX_DEVICES);
+-              return -EINVAL;
+-      }
++      /*
++       * we are already holding &mtd_table_mutex, but still need
++       * to bump refcount
++       */
++      err = __get_mtd_device(mtd);
++      if (err)
++              return;
+-      /* Create base sysfs directory and sysfs files */
+-      err = class_register(&ubi_class);
++      /* called while holding mtd_table_mutex */
++      mutex_lock_nested(&ubi_devices_mutex, SINGLE_DEPTH_NESTING);
++      err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false);
++      mutex_unlock(&ubi_devices_mutex);
+       if (err < 0)
+-              return err;
+-
+-      err = misc_register(&ubi_ctrl_cdev);
+-      if (err) {
+-              pr_err("UBI error: cannot register device\n");
+-              goto out;
+-      }
++              __put_mtd_device(mtd);
++}
+-      ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
+-                                            sizeof(struct ubi_wl_entry),
+-                                            0, 0, NULL);
+-      if (!ubi_wl_entry_slab) {
+-              err = -ENOMEM;
+-              goto out_dev_unreg;
+-      }
++static void ubi_notify_remove(struct mtd_info *mtd)
++{
++      /* do nothing for now */
++}
+-      err = ubi_debugfs_init();
+-      if (err)
+-              goto out_slab;
++static struct mtd_notifier ubi_mtd_notifier = {
++      .add = ubi_notify_add,
++      .remove = ubi_notify_remove,
++};
++static int __init ubi_init_attach(void)
++{
++      int err, i, k;
+       /* Attach MTD devices */
+       for (i = 0; i < mtd_devs; i++) {
+@@ -1298,25 +1299,79 @@ static int __init ubi_init(void)
+               }
+       }
++      return 0;
++
++out_detach:
++      for (k = 0; k < i; k++)
++              if (ubi_devices[k]) {
++                      mutex_lock(&ubi_devices_mutex);
++                      ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
++                      mutex_unlock(&ubi_devices_mutex);
++              }
++      return err;
++}
++#ifndef CONFIG_MTD_UBI_MODULE
++late_initcall(ubi_init_attach);
++#endif
++
++static int __init ubi_init(void)
++{
++      int err;
++
++      /* Ensure that EC and VID headers have correct size */
++      BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
++      BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
++
++      if (mtd_devs > UBI_MAX_DEVICES) {
++              pr_err("UBI error: too many MTD devices, maximum is %d\n",
++                     UBI_MAX_DEVICES);
++              return -EINVAL;
++      }
++
++      /* Create base sysfs directory and sysfs files */
++      err = class_register(&ubi_class);
++      if (err < 0)
++              return err;
++
++      err = misc_register(&ubi_ctrl_cdev);
++      if (err) {
++              pr_err("UBI error: cannot register device\n");
++              goto out;
++      }
++
++      ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
++                                            sizeof(struct ubi_wl_entry),
++                                            0, 0, NULL);
++      if (!ubi_wl_entry_slab) {
++              err = -ENOMEM;
++              goto out_dev_unreg;
++      }
++
++      err = ubi_debugfs_init();
++      if (err)
++              goto out_slab;
++
+       err = ubiblock_init();
+       if (err) {
+               pr_err("UBI error: block: cannot initialize, error %d\n", err);
+               /* See comment above re-ubi_is_module(). */
+               if (ubi_is_module())
+-                      goto out_detach;
++                      goto out_slab;
++      }
++
++      register_mtd_user(&ubi_mtd_notifier);
++
++      if (ubi_is_module()) {
++              err = ubi_init_attach();
++              if (err)
++                      goto out_mtd_notifier;
+       }
+       return 0;
+-out_detach:
+-      for (k = 0; k < i; k++)
+-              if (ubi_devices[k]) {
+-                      mutex_lock(&ubi_devices_mutex);
+-                      ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
+-                      mutex_unlock(&ubi_devices_mutex);
+-              }
+-      ubi_debugfs_exit();
++out_mtd_notifier:
++      unregister_mtd_user(&ubi_mtd_notifier);
+ out_slab:
+       kmem_cache_destroy(ubi_wl_entry_slab);
+ out_dev_unreg:
+@@ -1326,13 +1381,15 @@ out:
+       pr_err("UBI error: cannot initialize UBI, error %d\n", err);
+       return err;
+ }
+-late_initcall(ubi_init);
++device_initcall(ubi_init);
++
+ static void __exit ubi_exit(void)
+ {
+       int i;
+       ubiblock_exit();
++      unregister_mtd_user(&ubi_mtd_notifier);
+       for (i = 0; i < UBI_MAX_DEVICES; i++)
+               if (ubi_devices[i]) {
diff --git a/target/linux/generic/backport-6.6/405-v6.9-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch b/target/linux/generic/backport-6.6/405-v6.9-mtd-ubi-introduce-pre-removal-notification-for-UBI-v.patch
new file mode 100644 (file)
index 0000000..fb76ffc
--- /dev/null
@@ -0,0 +1,180 @@
+From 924731fbed3247e3b82b8ab17db587ee28c2e781 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:33:24 +0000
+Subject: [PATCH 5/8] mtd: ubi: introduce pre-removal notification for UBI
+ volumes
+
+Introduce a new notification type UBI_VOLUME_SHUTDOWN to inform users
+that a volume is just about to be removed.
+This is needed because users (such as the NVMEM subsystem) expect that
+at the time their removal function is called, the parenting device is
+still available (for removal of sysfs nodes, for example, in case of
+NVMEM which otherwise WARNs on volume removal).
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/build.c | 19 ++++++++++++++-----
+ drivers/mtd/ubi/kapi.c  |  2 +-
+ drivers/mtd/ubi/ubi.h   |  2 ++
+ drivers/mtd/ubi/vmt.c   | 17 +++++++++++++++--
+ include/linux/mtd/ubi.h |  2 ++
+ 5 files changed, 34 insertions(+), 8 deletions(-)
+
+--- a/drivers/mtd/ubi/build.c
++++ b/drivers/mtd/ubi/build.c
+@@ -91,7 +91,7 @@ static struct ubi_device *ubi_devices[UB
+ /* Serializes UBI devices creations and removals */
+ DEFINE_MUTEX(ubi_devices_mutex);
+-/* Protects @ubi_devices and @ubi->ref_count */
++/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */
+ static DEFINE_SPINLOCK(ubi_devices_lock);
+ /* "Show" method for files in '/<sysfs>/class/ubi/' */
+@@ -259,6 +259,9 @@ struct ubi_device *ubi_get_device(int ub
+       spin_lock(&ubi_devices_lock);
+       ubi = ubi_devices[ubi_num];
++      if (ubi && ubi->is_dead)
++              ubi = NULL;
++
+       if (ubi) {
+               ubi_assert(ubi->ref_count >= 0);
+               ubi->ref_count += 1;
+@@ -296,7 +299,7 @@ struct ubi_device *ubi_get_by_major(int
+       spin_lock(&ubi_devices_lock);
+       for (i = 0; i < UBI_MAX_DEVICES; i++) {
+               ubi = ubi_devices[i];
+-              if (ubi && MAJOR(ubi->cdev.dev) == major) {
++              if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
+                       ubi_assert(ubi->ref_count >= 0);
+                       ubi->ref_count += 1;
+                       get_device(&ubi->dev);
+@@ -325,7 +328,7 @@ int ubi_major2num(int major)
+       for (i = 0; i < UBI_MAX_DEVICES; i++) {
+               struct ubi_device *ubi = ubi_devices[i];
+-              if (ubi && MAJOR(ubi->cdev.dev) == major) {
++              if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
+                       ubi_num = ubi->ubi_num;
+                       break;
+               }
+@@ -512,7 +515,7 @@ static void ubi_free_volumes_from(struct
+       int i;
+       for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
+-              if (!ubi->volumes[i])
++              if (!ubi->volumes[i] || ubi->volumes[i]->is_dead)
+                       continue;
+               ubi_eba_replace_table(ubi->volumes[i], NULL);
+               ubi_fastmap_destroy_checkmap(ubi->volumes[i]);
+@@ -1094,7 +1097,6 @@ int ubi_detach_mtd_dev(int ubi_num, int
+               return -EINVAL;
+       spin_lock(&ubi_devices_lock);
+-      put_device(&ubi->dev);
+       ubi->ref_count -= 1;
+       if (ubi->ref_count) {
+               if (!anyway) {
+@@ -1105,6 +1107,13 @@ int ubi_detach_mtd_dev(int ubi_num, int
+               ubi_err(ubi, "%s reference count %d, destroy anyway",
+                       ubi->ubi_name, ubi->ref_count);
+       }
++      ubi->is_dead = true;
++      spin_unlock(&ubi_devices_lock);
++
++      ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL);
++
++      spin_lock(&ubi_devices_lock);
++      put_device(&ubi->dev);
+       ubi_devices[ubi_num] = NULL;
+       spin_unlock(&ubi_devices_lock);
+--- a/drivers/mtd/ubi/kapi.c
++++ b/drivers/mtd/ubi/kapi.c
+@@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume(
+       spin_lock(&ubi->volumes_lock);
+       vol = ubi->volumes[vol_id];
+-      if (!vol)
++      if (!vol || vol->is_dead)
+               goto out_unlock;
+       err = -EBUSY;
+--- a/drivers/mtd/ubi/ubi.h
++++ b/drivers/mtd/ubi/ubi.h
+@@ -345,6 +345,7 @@ struct ubi_volume {
+       int writers;
+       int exclusive;
+       int metaonly;
++      bool is_dead;
+       int reserved_pebs;
+       int vol_type;
+@@ -564,6 +565,7 @@ struct ubi_device {
+       spinlock_t volumes_lock;
+       int ref_count;
+       int image_seq;
++      bool is_dead;
+       int rsvd_pebs;
+       int avail_pebs;
+--- a/drivers/mtd/ubi/vmt.c
++++ b/drivers/mtd/ubi/vmt.c
+@@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct
+       struct ubi_device *ubi = vol->ubi;
+       spin_lock(&ubi->volumes_lock);
+-      if (!ubi->volumes[vol->vol_id]) {
++      if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) {
+               spin_unlock(&ubi->volumes_lock);
+               return -ENODEV;
+       }
+@@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device
+       /* Ensure that the name is unique */
+       for (i = 0; i < ubi->vtbl_slots; i++)
+-              if (ubi->volumes[i] &&
++              if (ubi->volumes[i] && !ubi->volumes[i]->is_dead &&
+                   ubi->volumes[i]->name_len == req->name_len &&
+                   !strcmp(ubi->volumes[i]->name, req->name)) {
+                       ubi_err(ubi, "volume \"%s\" exists (ID %d)",
+@@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_
+               err = -EBUSY;
+               goto out_unlock;
+       }
++
++      /*
++       * Mark volume as dead at this point to prevent that anyone
++       * can take a reference to the volume from now on.
++       * This is necessary as we have to release the spinlock before
++       * calling ubi_volume_notify.
++       */
++      vol->is_dead = true;
++      spin_unlock(&ubi->volumes_lock);
++
++      ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN);
++
++      spin_lock(&ubi->volumes_lock);
+       ubi->volumes[vol_id] = NULL;
+       spin_unlock(&ubi->volumes_lock);
+--- a/include/linux/mtd/ubi.h
++++ b/include/linux/mtd/ubi.h
+@@ -192,6 +192,7 @@ struct ubi_device_info {
+  *                    or a volume was removed)
+  * @UBI_VOLUME_RESIZED: a volume has been re-sized
+  * @UBI_VOLUME_RENAMED: a volume has been re-named
++ * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users
+  * @UBI_VOLUME_UPDATED: data has been written to a volume
+  *
+  * These constants define which type of event has happened when a volume
+@@ -202,6 +203,7 @@ enum {
+       UBI_VOLUME_REMOVED,
+       UBI_VOLUME_RESIZED,
+       UBI_VOLUME_RENAMED,
++      UBI_VOLUME_SHUTDOWN,
+       UBI_VOLUME_UPDATED,
+ };
diff --git a/target/linux/generic/backport-6.6/406-v6.9-mtd-ubi-populate-ubi-volume-fwnode.patch b/target/linux/generic/backport-6.6/406-v6.9-mtd-ubi-populate-ubi-volume-fwnode.patch
new file mode 100644 (file)
index 0000000..232e74d
--- /dev/null
@@ -0,0 +1,66 @@
+From 1c54542170819e36baa43c17ca55bb3d7da89a53 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:33:38 +0000
+Subject: [PATCH 6/8] mtd: ubi: populate ubi volume fwnode
+
+Look for the 'volumes' subnode of an MTD partition attached to a UBI
+device and attach matching child nodes to UBI volumes.
+This allows UBI volumes to be referenced in device tree, e.g. for use
+as NVMEM providers.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/vmt.c | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+--- a/drivers/mtd/ubi/vmt.c
++++ b/drivers/mtd/ubi/vmt.c
+@@ -124,6 +124,31 @@ static void vol_release(struct device *d
+       kfree(vol);
+ }
++static struct fwnode_handle *find_volume_fwnode(struct ubi_volume *vol)
++{
++      struct fwnode_handle *fw_vols, *fw_vol;
++      const char *volname;
++      u32 volid;
++
++      fw_vols = device_get_named_child_node(vol->dev.parent->parent, "volumes");
++      if (!fw_vols)
++              return NULL;
++
++      fwnode_for_each_child_node(fw_vols, fw_vol) {
++              if (!fwnode_property_read_string(fw_vol, "volname", &volname) &&
++                  strncmp(volname, vol->name, vol->name_len))
++                      continue;
++
++              if (!fwnode_property_read_u32(fw_vol, "volid", &volid) &&
++                  vol->vol_id != volid)
++                      continue;
++
++              return fw_vol;
++      }
++
++      return NULL;
++}
++
+ /**
+  * ubi_create_volume - create volume.
+  * @ubi: UBI device description object
+@@ -223,6 +248,7 @@ int ubi_create_volume(struct ubi_device
+       vol->name_len  = req->name_len;
+       memcpy(vol->name, req->name, vol->name_len);
+       vol->ubi = ubi;
++      device_set_node(&vol->dev, find_volume_fwnode(vol));
+       /*
+        * Finish all pending erases because there may be some LEBs belonging
+@@ -605,6 +631,7 @@ int ubi_add_volume(struct ubi_device *ub
+       vol->dev.class = &ubi_class;
+       vol->dev.groups = volume_dev_groups;
+       dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
++      device_set_node(&vol->dev, find_volume_fwnode(vol));
+       err = device_register(&vol->dev);
+       if (err) {
+               cdev_del(&vol->cdev);
diff --git a/target/linux/generic/backport-6.6/407-v6.9-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch b/target/linux/generic/backport-6.6/407-v6.9-mtd-ubi-provide-NVMEM-layer-over-UBI-volumes.patch
new file mode 100644 (file)
index 0000000..18fd1fb
--- /dev/null
@@ -0,0 +1,244 @@
+From 15fc7dc926c91c871f6c0305b2938dbdeb14203b Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 19 Dec 2023 02:33:48 +0000
+Subject: [PATCH 7/8] mtd: ubi: provide NVMEM layer over UBI volumes
+
+In an ideal world we would like UBI to be used where ever possible on a
+NAND chip. And with UBI support in ARM Trusted Firmware and U-Boot it
+is possible to achieve an (almost-)all-UBI flash layout. Hence the need
+for a way to also use UBI volumes to store board-level constants, such
+as MAC addresses and calibration data of wireless interfaces.
+
+Add UBI volume NVMEM driver module exposing UBI volumes as NVMEM
+providers. Allow UBI devices to have a "volumes" firmware subnode with
+volumes which may be compatible with "nvmem-cells".
+Access to UBI volumes via the NVMEM interface at this point is
+read-only, and it is slow, opening and closing the UBI volume for each
+access due to limitations of the NVMEM provider API.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/Kconfig  |  12 +++
+ drivers/mtd/ubi/Makefile |   1 +
+ drivers/mtd/ubi/nvmem.c  | 188 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 201 insertions(+)
+ create mode 100644 drivers/mtd/ubi/nvmem.c
+
+--- a/drivers/mtd/ubi/Kconfig
++++ b/drivers/mtd/ubi/Kconfig
+@@ -104,4 +104,16 @@ config MTD_UBI_BLOCK
+          If in doubt, say "N".
++config MTD_UBI_NVMEM
++      tristate "UBI virtual NVMEM"
++      default n
++      depends on NVMEM
++      help
++         This option enabled an additional driver exposing UBI volumes as NVMEM
++         providers, intended for platforms where UBI is part of the firmware
++         specification and used to store also e.g. MAC addresses or board-
++         specific Wi-Fi calibration data.
++
++         If in doubt, say "N".
++
+ endif # MTD_UBI
+--- a/drivers/mtd/ubi/Makefile
++++ b/drivers/mtd/ubi/Makefile
+@@ -7,3 +7,4 @@ ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap
+ ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o
+ obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
++obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o
+--- /dev/null
++++ b/drivers/mtd/ubi/nvmem.c
+@@ -0,0 +1,188 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Daniel Golle <daniel@makrotopia.org>
++ */
++
++/* UBI NVMEM provider */
++#include "ubi.h"
++#include <linux/nvmem-provider.h>
++#include <asm/div64.h>
++
++/* List of all NVMEM devices */
++static LIST_HEAD(nvmem_devices);
++static DEFINE_MUTEX(devices_mutex);
++
++struct ubi_nvmem {
++      struct nvmem_device *nvmem;
++      int ubi_num;
++      int vol_id;
++      int usable_leb_size;
++      struct list_head list;
++};
++
++static int ubi_nvmem_reg_read(void *priv, unsigned int from,
++                            void *val, size_t bytes)
++{
++      int err = 0, lnum = from, offs, bytes_left = bytes, to_read;
++      struct ubi_nvmem *unv = priv;
++      struct ubi_volume_desc *desc;
++
++      desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY);
++      if (IS_ERR(desc))
++              return PTR_ERR(desc);
++
++      offs = do_div(lnum, unv->usable_leb_size);
++      while (bytes_left) {
++              to_read = unv->usable_leb_size - offs;
++
++              if (to_read > bytes_left)
++                      to_read = bytes_left;
++
++              err = ubi_read(desc, lnum, val, offs, to_read);
++              if (err)
++                      break;
++
++              lnum += 1;
++              offs = 0;
++              bytes_left -= to_read;
++              val += to_read;
++      }
++      ubi_close_volume(desc);
++
++      if (err)
++              return err;
++
++      return bytes_left == 0 ? 0 : -EIO;
++}
++
++static int ubi_nvmem_add(struct ubi_volume_info *vi)
++{
++      struct device_node *np = dev_of_node(vi->dev);
++      struct nvmem_config config = {};
++      struct ubi_nvmem *unv;
++      int ret;
++
++      if (!np)
++              return 0;
++
++      if (!of_get_child_by_name(np, "nvmem-layout"))
++              return 0;
++
++      if (WARN_ON_ONCE(vi->usable_leb_size <= 0) ||
++          WARN_ON_ONCE(vi->size <= 0))
++              return -EINVAL;
++
++      unv = kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL);
++      if (!unv)
++              return -ENOMEM;
++
++      config.id = NVMEM_DEVID_NONE;
++      config.dev = vi->dev;
++      config.name = dev_name(vi->dev);
++      config.owner = THIS_MODULE;
++      config.priv = unv;
++      config.reg_read = ubi_nvmem_reg_read;
++      config.size = vi->usable_leb_size * vi->size;
++      config.word_size = 1;
++      config.stride = 1;
++      config.read_only = true;
++      config.root_only = true;
++      config.ignore_wp = true;
++      config.of_node = np;
++
++      unv->ubi_num = vi->ubi_num;
++      unv->vol_id = vi->vol_id;
++      unv->usable_leb_size = vi->usable_leb_size;
++      unv->nvmem = nvmem_register(&config);
++      if (IS_ERR(unv->nvmem)) {
++              ret = dev_err_probe(vi->dev, PTR_ERR(unv->nvmem),
++                                  "Failed to register NVMEM device\n");
++              kfree(unv);
++              return ret;
++      }
++
++      mutex_lock(&devices_mutex);
++      list_add_tail(&unv->list, &nvmem_devices);
++      mutex_unlock(&devices_mutex);
++
++      return 0;
++}
++
++static void ubi_nvmem_remove(struct ubi_volume_info *vi)
++{
++      struct ubi_nvmem *unv_c, *unv = NULL;
++
++      mutex_lock(&devices_mutex);
++      list_for_each_entry(unv_c, &nvmem_devices, list)
++              if (unv_c->ubi_num == vi->ubi_num && unv_c->vol_id == vi->vol_id) {
++                      unv = unv_c;
++                      break;
++              }
++
++      if (!unv) {
++              mutex_unlock(&devices_mutex);
++              return;
++      }
++
++      list_del(&unv->list);
++      mutex_unlock(&devices_mutex);
++      nvmem_unregister(unv->nvmem);
++      kfree(unv);
++}
++
++/**
++ * nvmem_notify - UBI notification handler.
++ * @nb: registered notifier block
++ * @l: notification type
++ * @ns_ptr: pointer to the &struct ubi_notification object
++ */
++static int nvmem_notify(struct notifier_block *nb, unsigned long l,
++                       void *ns_ptr)
++{
++      struct ubi_notification *nt = ns_ptr;
++
++      switch (l) {
++      case UBI_VOLUME_RESIZED:
++              ubi_nvmem_remove(&nt->vi);
++              fallthrough;
++      case UBI_VOLUME_ADDED:
++              ubi_nvmem_add(&nt->vi);
++              break;
++      case UBI_VOLUME_SHUTDOWN:
++              ubi_nvmem_remove(&nt->vi);
++              break;
++      default:
++              break;
++      }
++      return NOTIFY_OK;
++}
++
++static struct notifier_block nvmem_notifier = {
++      .notifier_call = nvmem_notify,
++};
++
++static int __init ubi_nvmem_init(void)
++{
++      return ubi_register_volume_notifier(&nvmem_notifier, 0);
++}
++
++static void __exit ubi_nvmem_exit(void)
++{
++      struct ubi_nvmem *unv, *tmp;
++
++      mutex_lock(&devices_mutex);
++      list_for_each_entry_safe(unv, tmp, &nvmem_devices, list) {
++              nvmem_unregister(unv->nvmem);
++              list_del(&unv->list);
++              kfree(unv);
++      }
++      mutex_unlock(&devices_mutex);
++
++      ubi_unregister_volume_notifier(&nvmem_notifier);
++}
++
++module_init(ubi_nvmem_init);
++module_exit(ubi_nvmem_exit);
++MODULE_DESCRIPTION("NVMEM layer over UBI volumes");
++MODULE_AUTHOR("Daniel Golle");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.6/408-v6.9-mtd-ubi-fix-NVMEM-over-UBI-volumes-on-32-bit-systems.patch b/target/linux/generic/backport-6.6/408-v6.9-mtd-ubi-fix-NVMEM-over-UBI-volumes-on-32-bit-systems.patch
new file mode 100644 (file)
index 0000000..8ce2b46
--- /dev/null
@@ -0,0 +1,34 @@
+From 04231c61dcd51db0f12061e49bb761b197109f2f Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 29 Feb 2024 03:47:24 +0000
+Subject: [PATCH 8/8] mtd: ubi: fix NVMEM over UBI volumes on 32-bit systems
+
+A compiler warning related to sizeof(int) != 8 when calling do_div()
+is triggered when building on 32-bit platforms.
+Address this by using integer types having a well-defined size.
+
+Fixes: 3ce485803da1 ("mtd: ubi: provide NVMEM layer over UBI volumes")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Zhihao Cheng <chengzhihao1@huawei.com>
+Tested-by: Randy Dunlap <rdunlap@infradead.org>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ drivers/mtd/ubi/nvmem.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/ubi/nvmem.c
++++ b/drivers/mtd/ubi/nvmem.c
+@@ -23,9 +23,12 @@ struct ubi_nvmem {
+ static int ubi_nvmem_reg_read(void *priv, unsigned int from,
+                             void *val, size_t bytes)
+ {
+-      int err = 0, lnum = from, offs, bytes_left = bytes, to_read;
++      size_t to_read, bytes_left = bytes;
+       struct ubi_nvmem *unv = priv;
+       struct ubi_volume_desc *desc;
++      uint32_t offs;
++      uint64_t lnum = from;
++      int err = 0;
+       desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY);
+       if (IS_ERR(desc))
diff --git a/target/linux/generic/backport-6.6/410-v6.13-01-block-add-support-for-defining-read-only-partitions.patch b/target/linux/generic/backport-6.6/410-v6.13-01-block-add-support-for-defining-read-only-partitions.patch
new file mode 100644 (file)
index 0000000..fabf16a
--- /dev/null
@@ -0,0 +1,53 @@
+From 03cb793b26834ddca170ba87057c8f883772dd45 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 3 Oct 2024 00:11:41 +0200
+Subject: [PATCH 1/5] block: add support for defining read-only partitions
+
+Add support for defining read-only partitions and complete support for
+it in the cmdline partition parser as the additional "ro" after a
+partition is scanned but never actually applied.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20241002221306.4403-2-ansuelsmth@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+---
+ block/blk.h                | 1 +
+ block/partitions/cmdline.c | 3 +++
+ block/partitions/core.c    | 3 +++
+ 3 files changed, 7 insertions(+)
+
+--- a/block/blk.h
++++ b/block/blk.h
+@@ -424,6 +424,7 @@ 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
+ 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/cmdline.c
++++ b/block/partitions/cmdline.c
+@@ -237,6 +237,9 @@ static int add_part(int slot, struct cmd
+       put_partition(state, slot, subpart->from >> 9,
+                     subpart->size >> 9);
++      if (subpart->flags & PF_RDONLY)
++              state->parts[slot].flags |= ADDPART_FLAG_READONLY;
++
+       info = &state->parts[slot].info;
+       strscpy(info->volname, subpart->name, sizeof(info->volname));
+--- a/block/partitions/core.c
++++ b/block/partitions/core.c
+@@ -392,6 +392,9 @@ static struct block_device *add_partitio
+                       goto out_del;
+       }
++      if (flags & ADDPART_FLAG_READONLY)
++              bdev->bd_read_only = true;
++
+       /* everything is up and running, commence */
+       err = xa_insert(&disk->part_tbl, partno, bdev, GFP_KERNEL);
+       if (err)
diff --git a/target/linux/generic/backport-6.6/410-v6.13-03-block-introduce-add_disk_fwnode.patch b/target/linux/generic/backport-6.6/410-v6.13-03-block-introduce-add_disk_fwnode.patch
new file mode 100644 (file)
index 0000000..41b51ab
--- /dev/null
@@ -0,0 +1,94 @@
+From e5f587242b6072ffab4f4a084a459a59f3035873 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 3 Oct 2024 00:11:43 +0200
+Subject: [PATCH 3/5] block: introduce add_disk_fwnode()
+
+Introduce add_disk_fwnode() as a replacement of device_add_disk() that
+permits to pass and attach a fwnode to disk dev.
+
+This variant can be useful for eMMC that might have the partition table
+for the disk defined in DT. A parser can later make use of the attached
+fwnode to parse the related table and init the hardcoded partition for
+the disk.
+
+device_add_disk() is converted to a simple wrapper of add_disk_fwnode()
+with the fwnode entry set as NULL.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20241002221306.4403-4-ansuelsmth@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+---
+ block/genhd.c          | 28 ++++++++++++++++++++++++----
+ include/linux/blkdev.h |  3 +++
+ 2 files changed, 27 insertions(+), 4 deletions(-)
+
+--- a/block/genhd.c
++++ b/block/genhd.c
+@@ -383,16 +383,18 @@ int disk_scan_partitions(struct gendisk
+ }
+ /**
+- * device_add_disk - add disk information to kernel list
++ * add_disk_fwnode - add disk information to kernel list with fwnode
+  * @parent: parent device for the disk
+  * @disk: per-device partitioning information
+  * @groups: Additional per-device sysfs groups
++ * @fwnode: attached disk fwnode
+  *
+  * This function registers the partitioning information in @disk
+- * with the kernel.
++ * with the kernel. Also attach a fwnode to the disk device.
+  */
+-int __must_check device_add_disk(struct device *parent, struct gendisk *disk,
+-                               const struct attribute_group **groups)
++int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk,
++                               const struct attribute_group **groups,
++                               struct fwnode_handle *fwnode)
+ {
+       struct device *ddev = disk_to_dev(disk);
+@@ -451,6 +453,8 @@ int __must_check device_add_disk(struct
+       ddev->parent = parent;
+       ddev->groups = groups;
+       dev_set_name(ddev, "%s", disk->disk_name);
++      if (fwnode)
++              device_set_node(ddev, fwnode);
+       if (!(disk->flags & GENHD_FL_HIDDEN))
+               ddev->devt = MKDEV(disk->major, disk->first_minor);
+       ret = device_add(ddev);
+@@ -552,6 +556,22 @@ out_exit_elevator:
+               elevator_exit(disk->queue);
+       return ret;
+ }
++EXPORT_SYMBOL_GPL(add_disk_fwnode);
++
++/**
++ * device_add_disk - add disk information to kernel list
++ * @parent: parent device for the disk
++ * @disk: per-device partitioning information
++ * @groups: Additional per-device sysfs groups
++ *
++ * This function registers the partitioning information in @disk
++ * with the kernel.
++ */
++int __must_check device_add_disk(struct device *parent, struct gendisk *disk,
++                               const struct attribute_group **groups)
++{
++      return add_disk_fwnode(parent, disk, groups, NULL);
++}
+ EXPORT_SYMBOL(device_add_disk);
+ static void blk_report_disk_dead(struct gendisk *disk, bool surprise)
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -741,6 +741,9 @@ static inline unsigned int blk_queue_dep
+ #define for_each_bio(_bio)            \
+       for (; _bio; _bio = _bio->bi_next)
++int __must_check add_disk_fwnode(struct device *parent, struct gendisk *disk,
++                               const struct attribute_group **groups,
++                               struct fwnode_handle *fwnode);
+ int __must_check device_add_disk(struct device *parent, struct gendisk *disk,
+                                const struct attribute_group **groups);
+ static inline int __must_check add_disk(struct gendisk *disk)
diff --git a/target/linux/generic/backport-6.6/410-v6.13-04-mmc-block-attach-partitions-fwnode-if-found-in-mmc-c.patch b/target/linux/generic/backport-6.6/410-v6.13-04-mmc-block-attach-partitions-fwnode-if-found-in-mmc-c.patch
new file mode 100644 (file)
index 0000000..cf0d18c
--- /dev/null
@@ -0,0 +1,104 @@
+From 45ff6c340ddfc2dade74d5b7a8962c778ab7042c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 3 Oct 2024 00:11:44 +0200
+Subject: [PATCH 4/5] mmc: block: attach partitions fwnode if found in mmc-card
+
+Attach partitions fwnode if found in mmc-card and register disk with it.
+
+This permits block partition to reference the node and register a
+partition table defined in DT for the special case for embedded device
+that doesn't have a partition table flashed but have an hardcoded
+partition table passed from the system.
+
+JEDEC BOOT partition boot0/boot1 are supported but in DT we refer with
+the JEDEC name of boot1 and boot2 to better adhere to documentation.
+
+Also JEDEC GP partition gp0/1/2/3 are supported but in DT we refer with
+the JEDEC name of gp1/2/3/4 to better adhere to documentration.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
+Link: https://lore.kernel.org/r/20241002221306.4403-5-ansuelsmth@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+---
+ drivers/mmc/core/block.c | 55 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 54 insertions(+), 1 deletion(-)
+
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -2455,6 +2455,56 @@ static inline int mmc_blk_readonly(struc
+              !(card->csd.cmdclass & CCC_BLOCK_WRITE);
+ }
++/*
++ * Search for a declared partitions node for the disk in mmc-card related node.
++ *
++ * This is to permit support for partition table defined in DT in special case
++ * where a partition table is not written in the disk and is expected to be
++ * passed from the running system.
++ *
++ * For the user disk, "partitions" node is searched.
++ * For the special HW disk, "partitions-" node with the appended name is used
++ * following this conversion table (to adhere to JEDEC naming)
++ * - boot0 -> partitions-boot1
++ * - boot1 -> partitions-boot2
++ * - gp0 -> partitions-gp1
++ * - gp1 -> partitions-gp2
++ * - gp2 -> partitions-gp3
++ * - gp3 -> partitions-gp4
++ */
++static struct fwnode_handle *mmc_blk_get_partitions_node(struct device *mmc_dev,
++                                                       const char *subname)
++{
++      const char *node_name = "partitions";
++
++      if (subname) {
++              mmc_dev = mmc_dev->parent;
++
++              /*
++               * Check if we are allocating a BOOT disk boot0/1 disk.
++               * In DT we use the JEDEC naming boot1/2.
++               */
++              if (!strcmp(subname, "boot0"))
++                      node_name = "partitions-boot1";
++              if (!strcmp(subname, "boot1"))
++                      node_name = "partitions-boot2";
++              /*
++               * Check if we are allocating a GP disk gp0/1/2/3 disk.
++               * In DT we use the JEDEC naming gp1/2/3/4.
++               */
++              if (!strcmp(subname, "gp0"))
++                      node_name = "partitions-gp1";
++              if (!strcmp(subname, "gp1"))
++                      node_name = "partitions-gp2";
++              if (!strcmp(subname, "gp2"))
++                      node_name = "partitions-gp3";
++              if (!strcmp(subname, "gp3"))
++                      node_name = "partitions-gp4";
++      }
++
++      return device_get_named_child_node(mmc_dev, node_name);
++}
++
+ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
+                                             struct device *parent,
+                                             sector_t size,
+@@ -2463,6 +2513,7 @@ static struct mmc_blk_data *mmc_blk_allo
+                                             int area_type,
+                                             unsigned int part_type)
+ {
++      struct fwnode_handle *disk_fwnode;
+       struct mmc_blk_data *md;
+       int devidx, ret;
+       char cap_str[10];
+@@ -2568,7 +2619,9 @@ static struct mmc_blk_data *mmc_blk_allo
+       /* used in ->open, must be set before add_disk: */
+       if (area_type == MMC_BLK_DATA_AREA_MAIN)
+               dev_set_drvdata(&card->dev, md);
+-      ret = device_add_disk(md->parent, md->disk, mmc_disk_attr_groups);
++      disk_fwnode = mmc_blk_get_partitions_node(parent, subname);
++      ret = add_disk_fwnode(md->parent, md->disk, mmc_disk_attr_groups,
++                            disk_fwnode);
+       if (ret)
+               goto err_put_disk;
+       return md;
diff --git a/target/linux/generic/backport-6.6/410-v6.13-05-block-add-support-for-partition-table-defined-in-OF.patch b/target/linux/generic/backport-6.6/410-v6.13-05-block-add-support-for-partition-table-defined-in-OF.patch
new file mode 100644 (file)
index 0000000..d260be1
--- /dev/null
@@ -0,0 +1,200 @@
+From 884555b557e5e6d41c866e2cd8d7b32f50ec974b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 3 Oct 2024 00:11:45 +0200
+Subject: [PATCH 5/5] block: add support for partition table defined in OF
+
+Add support for partition table defined in Device Tree. Similar to how
+it's done with MTD, add support for defining a fixed partition table in
+device tree.
+
+A common scenario for this is fixed block (eMMC) embedded devices that
+have no MBR or GPT partition table to save storage space. Bootloader
+access the block device with absolute address of data.
+
+This is to complete the functionality with an equivalent implementation
+with providing partition table with bootargs, for case where the booargs
+can't be modified and tweaking the Device Tree is the only solution to
+have an usabe partition table.
+
+The implementation follow the fixed-partitions parser used on MTD
+devices where a "partitions" node is expected to be declared with
+"fixed-partitions" compatible in the OF node of the disk device
+(mmc-card for eMMC for example) and each child node declare a label
+and a reg with offset and size. If label is not declared, the node name
+is used as fallback. Eventually is also possible to declare the read-only
+property to flag the partition as read-only.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/r/20241002221306.4403-6-ansuelsmth@gmail.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+---
+ block/partitions/Kconfig  |   9 ++++
+ block/partitions/Makefile |   1 +
+ block/partitions/check.h  |   1 +
+ block/partitions/core.c   |   3 ++
+ block/partitions/of.c     | 110 ++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 124 insertions(+)
+ create mode 100644 block/partitions/of.c
+
+--- a/block/partitions/Kconfig
++++ b/block/partitions/Kconfig
+@@ -270,4 +270,13 @@ config CMDLINE_PARTITION
+         Say Y here if you want to read the partition table from bootargs.
+         The format for the command line is just like mtdparts.
++config OF_PARTITION
++      bool "Device Tree partition support" if PARTITION_ADVANCED
++      depends on OF
++      help
++        Say Y here if you want to enable support for partition table
++        defined in Device Tree. (mainly for eMMC)
++        The format for the device tree node is just like MTD fixed-partition
++        schema.
++
+ endmenu
+--- a/block/partitions/Makefile
++++ b/block/partitions/Makefile
+@@ -12,6 +12,7 @@ obj-$(CONFIG_CMDLINE_PARTITION) += cmdli
+ obj-$(CONFIG_MAC_PARTITION) += mac.o
+ obj-$(CONFIG_LDM_PARTITION) += ldm.o
+ obj-$(CONFIG_MSDOS_PARTITION) += msdos.o
++obj-$(CONFIG_OF_PARTITION) += of.o
+ obj-$(CONFIG_OSF_PARTITION) += osf.o
+ obj-$(CONFIG_SGI_PARTITION) += sgi.o
+ obj-$(CONFIG_SUN_PARTITION) += sun.o
+--- a/block/partitions/check.h
++++ b/block/partitions/check.h
+@@ -62,6 +62,7 @@ int karma_partition(struct parsed_partit
+ int ldm_partition(struct parsed_partitions *state);
+ int mac_partition(struct parsed_partitions *state);
+ int msdos_partition(struct parsed_partitions *state);
++int of_partition(struct parsed_partitions *state);
+ int osf_partition(struct parsed_partitions *state);
+ int sgi_partition(struct parsed_partitions *state);
+ int sun_partition(struct parsed_partitions *state);
+--- a/block/partitions/core.c
++++ b/block/partitions/core.c
+@@ -43,6 +43,9 @@ static int (*const check_part[])(struct
+ #ifdef CONFIG_CMDLINE_PARTITION
+       cmdline_partition,
+ #endif
++#ifdef CONFIG_OF_PARTITION
++      of_partition,           /* cmdline have priority to OF */
++#endif
+ #ifdef CONFIG_EFI_PARTITION
+       efi_partition,          /* this must come before msdos */
+ #endif
+--- /dev/null
++++ b/block/partitions/of.c
+@@ -0,0 +1,110 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <linux/blkdev.h>
++#include <linux/major.h>
++#include <linux/of.h>
++#include <linux/string.h>
++#include "check.h"
++
++static int validate_of_partition(struct device_node *np, int slot)
++{
++      u64 offset, size;
++      int len;
++
++      const __be32 *reg = of_get_property(np, "reg", &len);
++      int a_cells = of_n_addr_cells(np);
++      int s_cells = of_n_size_cells(np);
++
++      /* Make sure reg len match the expected addr and size cells */
++      if (len / sizeof(*reg) != a_cells + s_cells)
++              return -EINVAL;
++
++      /* Validate offset conversion from bytes to sectors */
++      offset = of_read_number(reg, a_cells);
++      if (offset % SECTOR_SIZE)
++              return -EINVAL;
++
++      /* Validate size conversion from bytes to sectors */
++      size = of_read_number(reg + a_cells, s_cells);
++      if (!size || size % SECTOR_SIZE)
++              return -EINVAL;
++
++      return 0;
++}
++
++static void add_of_partition(struct parsed_partitions *state, int slot,
++                           struct device_node *np)
++{
++      struct partition_meta_info *info;
++      char tmp[sizeof(info->volname) + 4];
++      const char *partname;
++      int len;
++
++      const __be32 *reg = of_get_property(np, "reg", &len);
++      int a_cells = of_n_addr_cells(np);
++      int s_cells = of_n_size_cells(np);
++
++      /* Convert bytes to sector size */
++      u64 offset = of_read_number(reg, a_cells) / SECTOR_SIZE;
++      u64 size = of_read_number(reg + a_cells, s_cells) / SECTOR_SIZE;
++
++      put_partition(state, slot, offset, size);
++
++      if (of_property_read_bool(np, "read-only"))
++              state->parts[slot].flags |= ADDPART_FLAG_READONLY;
++
++      /*
++       * Follow MTD label logic, search for label property,
++       * fallback to node name if not found.
++       */
++      info = &state->parts[slot].info;
++      partname = of_get_property(np, "label", &len);
++      if (!partname)
++              partname = of_get_property(np, "name", &len);
++      strscpy(info->volname, partname, sizeof(info->volname));
++
++      snprintf(tmp, sizeof(tmp), "(%s)", info->volname);
++      strlcat(state->pp_buf, tmp, PAGE_SIZE);
++}
++
++int of_partition(struct parsed_partitions *state)
++{
++      struct device *ddev = disk_to_dev(state->disk);
++      struct device_node *np;
++      int slot;
++
++      struct device_node *partitions_np = of_node_get(ddev->of_node);
++
++      if (!partitions_np ||
++          !of_device_is_compatible(partitions_np, "fixed-partitions"))
++              return 0;
++
++      slot = 1;
++      /* Validate parition offset and size */
++      for_each_child_of_node(partitions_np, np) {
++              if (validate_of_partition(np, slot)) {
++                      of_node_put(np);
++                      of_node_put(partitions_np);
++
++                      return -1;
++              }
++
++              slot++;
++      }
++
++      slot = 1;
++      for_each_child_of_node(partitions_np, np) {
++              if (slot >= state->limit) {
++                      of_node_put(np);
++                      break;
++              }
++
++              add_of_partition(state, slot, np);
++
++              slot++;
++      }
++
++      strlcat(state->pp_buf, "\n", PAGE_SIZE);
++
++      return 1;
++}
diff --git a/target/linux/generic/backport-6.6/411-v6.7-mtd-spinand-add-support-for-FORESEE-F35SQA002G.patch b/target/linux/generic/backport-6.6/411-v6.7-mtd-spinand-add-support-for-FORESEE-F35SQA002G.patch
new file mode 100644 (file)
index 0000000..90672b9
--- /dev/null
@@ -0,0 +1,146 @@
+From 49c8e854869d673df8452f24dfa8989cd0f615a8 Mon Sep 17 00:00:00 2001
+From: Martin Kurbanov <mmkurbanov@salutedevices.com>
+Date: Mon, 2 Oct 2023 17:04:58 +0300
+Subject: [PATCH] mtd: spinand: add support for FORESEE F35SQA002G
+
+Add support for FORESEE F35SQA002G SPI NAND.
+Datasheet:
+  https://www.longsys.com/uploads/LM-00006FORESEEF35SQA002GDatasheet_1650183701.pdf
+
+Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20231002140458.147605-1-mmkurbanov@salutedevices.com
+---
+ drivers/mtd/nand/spi/Makefile  |  2 +-
+ drivers/mtd/nand/spi/core.c    |  1 +
+ drivers/mtd/nand/spi/foresee.c | 95 ++++++++++++++++++++++++++++++++++
+ include/linux/mtd/spinand.h    |  1 +
+ 4 files changed, 98 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/mtd/nand/spi/foresee.c
+
+--- a/drivers/mtd/nand/spi/Makefile
++++ b/drivers/mtd/nand/spi/Makefile
+@@ -1,4 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0
+-spinand-objs := core.o alliancememory.o ato.o esmt.o gigadevice.o macronix.o
++spinand-objs := core.o alliancememory.o ato.o esmt.o foresee.o gigadevice.o macronix.o
+ spinand-objs += 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
+@@ -940,6 +940,7 @@ static const struct spinand_manufacturer
+       &alliancememory_spinand_manufacturer,
+       &ato_spinand_manufacturer,
+       &esmt_c8_spinand_manufacturer,
++      &foresee_spinand_manufacturer,
+       &gigadevice_spinand_manufacturer,
+       &macronix_spinand_manufacturer,
+       &micron_spinand_manufacturer,
+--- /dev/null
++++ b/drivers/mtd/nand/spi/foresee.c
+@@ -0,0 +1,95 @@
++// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
++/*
++ * Copyright (c) 2023, SberDevices. All Rights Reserved.
++ *
++ * Author: Martin Kurbanov <mmkurbanov@salutedevices.com>
++ */
++
++#include <linux/device.h>
++#include <linux/kernel.h>
++#include <linux/mtd/spinand.h>
++
++#define SPINAND_MFR_FORESEE           0xCD
++
++static SPINAND_OP_VARIANTS(read_cache_variants,
++              SPINAND_PAGE_READ_FROM_CACHE_X4_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 f35sqa002g_ooblayout_ecc(struct mtd_info *mtd, int section,
++                                  struct mtd_oob_region *region)
++{
++      return -ERANGE;
++}
++
++static int f35sqa002g_ooblayout_free(struct mtd_info *mtd, int section,
++                                   struct mtd_oob_region *region)
++{
++      if (section)
++              return -ERANGE;
++
++      /* Reserve 2 bytes for the BBM. */
++      region->offset = 2;
++      region->length = 62;
++
++      return 0;
++}
++
++static const struct mtd_ooblayout_ops f35sqa002g_ooblayout = {
++      .ecc = f35sqa002g_ooblayout_ecc,
++      .free = f35sqa002g_ooblayout_free,
++};
++
++static int f35sqa002g_ecc_get_status(struct spinand_device *spinand, u8 status)
++{
++      struct nand_device *nand = spinand_to_nand(spinand);
++
++      switch (status & STATUS_ECC_MASK) {
++      case STATUS_ECC_NO_BITFLIPS:
++              return 0;
++
++      case STATUS_ECC_HAS_BITFLIPS:
++              return nanddev_get_ecc_conf(nand)->strength;
++
++      default:
++              break;
++      }
++
++      /* More than 1-bit error was detected in one or more sectors and
++       * cannot be corrected.
++       */
++      return -EBADMSG;
++}
++
++static const struct spinand_info foresee_spinand_table[] = {
++      SPINAND_INFO("F35SQA002G",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x72, 0x72),
++                   NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
++                   NAND_ECCREQ(1, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&f35sqa002g_ooblayout,
++                                   f35sqa002g_ecc_get_status)),
++};
++
++static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = {
++};
++
++const struct spinand_manufacturer foresee_spinand_manufacturer = {
++      .id = SPINAND_MFR_FORESEE,
++      .name = "FORESEE",
++      .chips = foresee_spinand_table,
++      .nchips = ARRAY_SIZE(foresee_spinand_table),
++      .ops = &foresee_spinand_manuf_ops,
++};
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -263,6 +263,7 @@ struct spinand_manufacturer {
+ extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
+ extern const struct spinand_manufacturer ato_spinand_manufacturer;
+ extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
++extern const struct spinand_manufacturer foresee_spinand_manufacturer;
+ extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
+ extern const struct spinand_manufacturer macronix_spinand_manufacturer;
+ extern const struct spinand_manufacturer micron_spinand_manufacturer;
diff --git a/target/linux/generic/backport-6.6/412-v6.14-mtd-spinand-add-support-for-FORESEE-F35SQA001G.patch b/target/linux/generic/backport-6.6/412-v6.14-mtd-spinand-add-support-for-FORESEE-F35SQA001G.patch
new file mode 100644 (file)
index 0000000..84b3b2a
--- /dev/null
@@ -0,0 +1,38 @@
+From ae461cde5c559675fc4c0ba351c7c31ace705f56 Mon Sep 17 00:00:00 2001
+From: Bohdan Chubuk <chbgdn@gmail.com>
+Date: Sun, 10 Nov 2024 22:50:47 +0200
+Subject: [PATCH] mtd: spinand: add support for FORESEE F35SQA001G
+
+Add support for FORESEE F35SQA001G SPI NAND.
+
+Similar to F35SQA002G, but differs in capacity.
+Datasheet:
+  -  https://cdn.ozdisan.com/ETicaret_Dosya/704795_871495.pdf
+
+Tested on Xiaomi AX3000T flashed with OpenWRT.
+
+Signed-off-by: Bohdan Chubuk <chbgdn@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/mtd/nand/spi/foresee.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/nand/spi/foresee.c
++++ b/drivers/mtd/nand/spi/foresee.c
+@@ -81,6 +81,16 @@ static const struct spinand_info foresee
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&f35sqa002g_ooblayout,
+                                    f35sqa002g_ecc_get_status)),
++      SPINAND_INFO("F35SQA001G",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x71, 0x71),
++                   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),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&f35sqa002g_ooblayout,
++                                   f35sqa002g_ecc_get_status)),
+ };
+ static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = {
diff --git a/target/linux/generic/backport-6.6/600-v6.10-net-Remove-conditional-threaded-NAPI-wakeup-based-on.patch b/target/linux/generic/backport-6.6/600-v6.10-net-Remove-conditional-threaded-NAPI-wakeup-based-on.patch
new file mode 100644 (file)
index 0000000..6ee4716
--- /dev/null
@@ -0,0 +1,75 @@
+From 56364c910691f6d10ba88c964c9041b9ab777bd6 Mon Sep 17 00:00:00 2001
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Date: Mon, 25 Mar 2024 08:40:28 +0100
+Subject: [PATCH 1/4] net: Remove conditional threaded-NAPI wakeup based on
+ task state.
+
+A NAPI thread is scheduled by first setting NAPI_STATE_SCHED bit. If
+successful (the bit was not yet set) then the NAPI_STATE_SCHED_THREADED
+is set but only if thread's state is not TASK_INTERRUPTIBLE (is
+TASK_RUNNING) followed by task wakeup.
+
+If the task is idle (TASK_INTERRUPTIBLE) then the
+NAPI_STATE_SCHED_THREADED bit is not set. The thread is no relying on
+the bit but always leaving the wait-loop after returning from schedule()
+because there must have been a wakeup.
+
+The smpboot-threads implementation for per-CPU threads requires an
+explicit condition and does not support "if we get out of schedule()
+then there must be something to do".
+
+Removing this optimisation simplifies the following integration.
+
+Set NAPI_STATE_SCHED_THREADED unconditionally on wakeup and rely on it
+in the wait path by removing the `woken' condition.
+
+Acked-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ net/core/dev.c | 14 ++------------
+ 1 file changed, 2 insertions(+), 12 deletions(-)
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -4481,13 +4481,7 @@ static inline void ____napi_schedule(str
+                */
+               thread = READ_ONCE(napi->thread);
+               if (thread) {
+-                      /* Avoid doing set_bit() if the thread is in
+-                       * INTERRUPTIBLE state, cause napi_thread_wait()
+-                       * makes sure to proceed with napi polling
+-                       * if the thread is explicitly woken from here.
+-                       */
+-                      if (READ_ONCE(thread->__state) != TASK_INTERRUPTIBLE)
+-                              set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
++                      set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
+                       wake_up_process(thread);
+                       return;
+               }
+@@ -6643,8 +6637,6 @@ static int napi_poll(struct napi_struct
+ static int napi_thread_wait(struct napi_struct *napi)
+ {
+-      bool woken = false;
+-
+       set_current_state(TASK_INTERRUPTIBLE);
+       while (!kthread_should_stop()) {
+@@ -6653,15 +6645,13 @@ static int napi_thread_wait(struct napi_
+                * Testing SCHED bit is not enough because SCHED bit might be
+                * set by some other busy poll thread or by napi_disable().
+                */
+-              if (test_bit(NAPI_STATE_SCHED_THREADED, &napi->state) || woken) {
++              if (test_bit(NAPI_STATE_SCHED_THREADED, &napi->state)) {
+                       WARN_ON(!list_empty(&napi->poll_list));
+                       __set_current_state(TASK_RUNNING);
+                       return 0;
+               }
+               schedule();
+-              /* woken being true indicates this thread owns this napi. */
+-              woken = true;
+               set_current_state(TASK_INTERRUPTIBLE);
+       }
+       __set_current_state(TASK_RUNNING);
diff --git a/target/linux/generic/backport-6.6/601-v6.10-net-Allow-to-use-SMP-threads-for-backlog-NAPI.patch b/target/linux/generic/backport-6.6/601-v6.10-net-Allow-to-use-SMP-threads-for-backlog-NAPI.patch
new file mode 100644 (file)
index 0000000..8c0b41f
--- /dev/null
@@ -0,0 +1,330 @@
+From dad6b97702639fba27a2bd3e986982ad6f0db3a7 Mon Sep 17 00:00:00 2001
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Date: Mon, 25 Mar 2024 08:40:29 +0100
+Subject: [PATCH 2/4] net: Allow to use SMP threads for backlog NAPI.
+
+Backlog NAPI is a per-CPU NAPI struct only (with no device behind it)
+used by drivers which don't do NAPI them self, RPS and parts of the
+stack which need to avoid recursive deadlocks while processing a packet.
+
+The non-NAPI driver use the CPU local backlog NAPI. If RPS is enabled
+then a flow for the skb is computed and based on the flow the skb can be
+enqueued on a remote CPU. Scheduling/ raising the softirq (for backlog's
+NAPI) on the remote CPU isn't trivial because the softirq is only
+scheduled on the local CPU and performed after the hardirq is done.
+In order to schedule a softirq on the remote CPU, an IPI is sent to the
+remote CPU which schedules the backlog-NAPI on the then local CPU.
+
+On PREEMPT_RT interrupts are force-threaded. The soft interrupts are
+raised within the interrupt thread and processed after the interrupt
+handler completed still within the context of the interrupt thread. The
+softirq is handled in the context where it originated.
+
+With force-threaded interrupts enabled, ksoftirqd is woken up if a
+softirq is raised from hardirq context. This is the case if it is raised
+from an IPI. Additionally there is a warning on PREEMPT_RT if the
+softirq is raised from the idle thread.
+This was done for two reasons:
+- With threaded interrupts the processing should happen in thread
+  context (where it originated) and ksoftirqd is the only thread for
+  this context if raised from hardirq. Using the currently running task
+  instead would "punish" a random task.
+- Once ksoftirqd is active it consumes all further softirqs until it
+  stops running. This changed recently and is no longer the case.
+
+Instead of keeping the backlog NAPI in ksoftirqd (in force-threaded/
+PREEMPT_RT setups) I am proposing NAPI-threads for backlog.
+The "proper" setup with threaded-NAPI is not doable because the threads
+are not pinned to an individual CPU and can be modified by the user.
+Additionally a dummy network device would have to be assigned. Also
+CPU-hotplug has to be considered if additional CPUs show up.
+All this can be probably done/ solved but the smpboot-threads already
+provide this infrastructure.
+
+Sending UDP packets over loopback expects that the packet is processed
+within the call. Delaying it by handing it over to the thread hurts
+performance. It is not beneficial to the outcome if the context switch
+happens immediately after enqueue or after a while to process a few
+packets in a batch.
+There is no need to always use the thread if the backlog NAPI is
+requested on the local CPU. This restores the loopback throuput. The
+performance drops mostly to the same value after enabling RPS on the
+loopback comparing the IPI and the tread result.
+
+Create NAPI-threads for backlog if request during boot. The thread runs
+the inner loop from napi_threaded_poll(), the wait part is different. It
+checks for NAPI_STATE_SCHED (the backlog NAPI can not be disabled).
+
+The NAPI threads for backlog are optional, it has to be enabled via the boot
+argument "thread_backlog_napi". It is mandatory for PREEMPT_RT to avoid the
+wakeup of ksoftirqd from the IPI.
+
+Acked-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ net/core/dev.c | 148 +++++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 113 insertions(+), 35 deletions(-)
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -78,6 +78,7 @@
+ #include <linux/slab.h>
+ #include <linux/sched.h>
+ #include <linux/sched/mm.h>
++#include <linux/smpboot.h>
+ #include <linux/mutex.h>
+ #include <linux/rwsem.h>
+ #include <linux/string.h>
+@@ -217,6 +218,31 @@ static inline struct hlist_head *dev_ind
+       return &net->dev_index_head[ifindex & (NETDEV_HASHENTRIES - 1)];
+ }
++#ifndef CONFIG_PREEMPT_RT
++
++static DEFINE_STATIC_KEY_FALSE(use_backlog_threads_key);
++
++static int __init setup_backlog_napi_threads(char *arg)
++{
++      static_branch_enable(&use_backlog_threads_key);
++      return 0;
++}
++early_param("thread_backlog_napi", setup_backlog_napi_threads);
++
++static bool use_backlog_threads(void)
++{
++      return static_branch_unlikely(&use_backlog_threads_key);
++}
++
++#else
++
++static bool use_backlog_threads(void)
++{
++      return true;
++}
++
++#endif
++
+ static inline void rps_lock_irqsave(struct softnet_data *sd,
+                                   unsigned long *flags)
+ {
+@@ -4449,6 +4475,7 @@ EXPORT_SYMBOL(__dev_direct_xmit);
+ /*************************************************************************
+  *                    Receiver routines
+  *************************************************************************/
++static DEFINE_PER_CPU(struct task_struct *, backlog_napi);
+ int netdev_max_backlog __read_mostly = 1000;
+ EXPORT_SYMBOL(netdev_max_backlog);
+@@ -4481,12 +4508,16 @@ static inline void ____napi_schedule(str
+                */
+               thread = READ_ONCE(napi->thread);
+               if (thread) {
++                      if (use_backlog_threads() && thread == raw_cpu_read(backlog_napi))
++                              goto use_local_napi;
++
+                       set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
+                       wake_up_process(thread);
+                       return;
+               }
+       }
++use_local_napi:
+       list_add_tail(&napi->poll_list, &sd->poll_list);
+       WRITE_ONCE(napi->list_owner, smp_processor_id());
+       /* If not called from net_rx_action()
+@@ -4732,6 +4763,11 @@ static void napi_schedule_rps(struct sof
+ #ifdef CONFIG_RPS
+       if (sd != mysd) {
++              if (use_backlog_threads()) {
++                      __napi_schedule_irqoff(&sd->backlog);
++                      return;
++              }
++
+               sd->rps_ipi_next = mysd->rps_ipi_list;
+               mysd->rps_ipi_list = sd;
+@@ -5955,7 +5991,7 @@ static void net_rps_action_and_irq_enabl
+ #ifdef CONFIG_RPS
+       struct softnet_data *remsd = sd->rps_ipi_list;
+-      if (remsd) {
++      if (!use_backlog_threads() && remsd) {
+               sd->rps_ipi_list = NULL;
+               local_irq_enable();
+@@ -5970,7 +6006,7 @@ static void net_rps_action_and_irq_enabl
+ static bool sd_has_rps_ipi_waiting(struct softnet_data *sd)
+ {
+ #ifdef CONFIG_RPS
+-      return sd->rps_ipi_list != NULL;
++      return !use_backlog_threads() && sd->rps_ipi_list;
+ #else
+       return false;
+ #endif
+@@ -6014,7 +6050,7 @@ static int process_backlog(struct napi_s
+                        * We can use a plain write instead of clear_bit(),
+                        * and we dont need an smp_mb() memory barrier.
+                        */
+-                      napi->state = 0;
++                      napi->state &= NAPIF_STATE_THREADED;
+                       again = false;
+               } else {
+                       skb_queue_splice_tail_init(&sd->input_pkt_queue,
+@@ -6680,43 +6716,48 @@ static void skb_defer_free_flush(struct
+       }
+ }
+-static int napi_threaded_poll(void *data)
++static void napi_threaded_poll_loop(struct napi_struct *napi)
+ {
+-      struct napi_struct *napi = data;
+       struct softnet_data *sd;
+-      void *have;
++      unsigned long last_qs = jiffies;
+-      while (!napi_thread_wait(napi)) {
+-              unsigned long last_qs = jiffies;
++      for (;;) {
++              bool repoll = false;
++              void *have;
+-              for (;;) {
+-                      bool repoll = false;
++              local_bh_disable();
++              sd = this_cpu_ptr(&softnet_data);
++              sd->in_napi_threaded_poll = true;
+-                      local_bh_disable();
+-                      sd = this_cpu_ptr(&softnet_data);
+-                      sd->in_napi_threaded_poll = true;
+-
+-                      have = netpoll_poll_lock(napi);
+-                      __napi_poll(napi, &repoll);
+-                      netpoll_poll_unlock(have);
+-
+-                      sd->in_napi_threaded_poll = false;
+-                      barrier();
+-
+-                      if (sd_has_rps_ipi_waiting(sd)) {
+-                              local_irq_disable();
+-                              net_rps_action_and_irq_enable(sd);
+-                      }
+-                      skb_defer_free_flush(sd);
+-                      local_bh_enable();
++              have = netpoll_poll_lock(napi);
++              __napi_poll(napi, &repoll);
++              netpoll_poll_unlock(have);
++
++              sd->in_napi_threaded_poll = false;
++              barrier();
++
++              if (sd_has_rps_ipi_waiting(sd)) {
++                      local_irq_disable();
++                      net_rps_action_and_irq_enable(sd);
++              }
++              skb_defer_free_flush(sd);
++              local_bh_enable();
+-                      if (!repoll)
+-                              break;
++              if (!repoll)
++                      break;
+-                      rcu_softirq_qs_periodic(last_qs);
+-                      cond_resched();
+-              }
++              rcu_softirq_qs_periodic(last_qs);
++              cond_resched();
+       }
++}
++
++static int napi_threaded_poll(void *data)
++{
++      struct napi_struct *napi = data;
++
++      while (!napi_thread_wait(napi))
++              napi_threaded_poll_loop(napi);
++
+       return 0;
+ }
+@@ -11297,7 +11338,7 @@ static int dev_cpu_dead(unsigned int old
+               list_del_init(&napi->poll_list);
+               if (napi->poll == process_backlog)
+-                      napi->state = 0;
++                      napi->state &= NAPIF_STATE_THREADED;
+               else
+                       ____napi_schedule(sd, napi);
+       }
+@@ -11305,12 +11346,14 @@ static int dev_cpu_dead(unsigned int old
+       raise_softirq_irqoff(NET_TX_SOFTIRQ);
+       local_irq_enable();
++      if (!use_backlog_threads()) {
+ #ifdef CONFIG_RPS
+-      remsd = oldsd->rps_ipi_list;
+-      oldsd->rps_ipi_list = NULL;
++              remsd = oldsd->rps_ipi_list;
++              oldsd->rps_ipi_list = NULL;
+ #endif
+-      /* send out pending IPI's on offline CPU */
+-      net_rps_send_ipi(remsd);
++              /* send out pending IPI's on offline CPU */
++              net_rps_send_ipi(remsd);
++      }
+       /* Process offline CPU's input_pkt_queue */
+       while ((skb = __skb_dequeue(&oldsd->process_queue))) {
+@@ -11573,6 +11616,38 @@ static struct pernet_operations __net_in
+  *
+  */
++static int backlog_napi_should_run(unsigned int cpu)
++{
++      struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu);
++      struct napi_struct *napi = &sd->backlog;
++
++      return test_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
++}
++
++static void run_backlog_napi(unsigned int cpu)
++{
++      struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu);
++
++      napi_threaded_poll_loop(&sd->backlog);
++}
++
++static void backlog_napi_setup(unsigned int cpu)
++{
++      struct softnet_data *sd = per_cpu_ptr(&softnet_data, cpu);
++      struct napi_struct *napi = &sd->backlog;
++
++      napi->thread = this_cpu_read(backlog_napi);
++      set_bit(NAPI_STATE_THREADED, &napi->state);
++}
++
++static struct smp_hotplug_thread backlog_threads = {
++      .store                  = &backlog_napi,
++      .thread_should_run      = backlog_napi_should_run,
++      .thread_fn              = run_backlog_napi,
++      .thread_comm            = "backlog_napi/%u",
++      .setup                  = backlog_napi_setup,
++};
++
+ /*
+  *       This is called single threaded during boot, so no need
+  *       to take the rtnl semaphore.
+@@ -11623,7 +11698,10 @@ static int __init net_dev_init(void)
+               init_gro_hash(&sd->backlog);
+               sd->backlog.poll = process_backlog;
+               sd->backlog.weight = weight_p;
++              INIT_LIST_HEAD(&sd->backlog.poll_list);
+       }
++      if (use_backlog_threads())
++              smpboot_register_percpu_thread(&backlog_threads);
+       dev_boot_phase = 0;
diff --git a/target/linux/generic/backport-6.6/602-v6.10-net-Use-backlog-NAPI-to-clean-up-the-defer_list.patch b/target/linux/generic/backport-6.6/602-v6.10-net-Use-backlog-NAPI-to-clean-up-the-defer_list.patch
new file mode 100644 (file)
index 0000000..3fcc72e
--- /dev/null
@@ -0,0 +1,121 @@
+From 80d2eefcb4c84aa9018b2a997ab3a4c567bc821a Mon Sep 17 00:00:00 2001
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Date: Mon, 25 Mar 2024 08:40:30 +0100
+Subject: [PATCH 3/4] net: Use backlog-NAPI to clean up the defer_list.
+
+The defer_list is a per-CPU list which is used to free skbs outside of
+the socket lock and on the CPU on which they have been allocated.
+The list is processed during NAPI callbacks so ideally the list is
+cleaned up.
+Should the amount of skbs on the list exceed a certain water mark then
+the softirq is triggered remotely on the target CPU by invoking a remote
+function call. The raise of the softirqs via a remote function call
+leads to waking the ksoftirqd on PREEMPT_RT which is undesired.
+The backlog-NAPI threads already provide the infrastructure which can be
+utilized to perform the cleanup of the defer_list.
+
+The NAPI state is updated with the input_pkt_queue.lock acquired. It
+order not to break the state, it is needed to also wake the backlog-NAPI
+thread with the lock held. This requires to acquire the use the lock in
+rps_lock_irq*() if the backlog-NAPI threads are used even with RPS
+disabled.
+
+Move the logic of remotely starting softirqs to clean up the defer_list
+into kick_defer_list_purge(). Make sure a lock is held in
+rps_lock_irq*() if backlog-NAPI threads are used. Schedule backlog-NAPI
+for defer_list cleanup if backlog-NAPI is available.
+
+Acked-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ include/linux/netdevice.h |  1 +
+ net/core/dev.c            | 25 +++++++++++++++++++++----
+ net/core/skbuff.c         |  4 ++--
+ 3 files changed, 24 insertions(+), 6 deletions(-)
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -3300,6 +3300,7 @@ static inline void dev_xmit_recursion_de
+       __this_cpu_dec(softnet_data.xmit.recursion);
+ }
++void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu);
+ void __netif_schedule(struct Qdisc *q);
+ void netif_schedule_queue(struct netdev_queue *txq);
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -246,7 +246,7 @@ static bool use_backlog_threads(void)
+ static inline void rps_lock_irqsave(struct softnet_data *sd,
+                                   unsigned long *flags)
+ {
+-      if (IS_ENABLED(CONFIG_RPS))
++      if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
+               spin_lock_irqsave(&sd->input_pkt_queue.lock, *flags);
+       else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
+               local_irq_save(*flags);
+@@ -254,7 +254,7 @@ static inline void rps_lock_irqsave(stru
+ static inline void rps_lock_irq_disable(struct softnet_data *sd)
+ {
+-      if (IS_ENABLED(CONFIG_RPS))
++      if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
+               spin_lock_irq(&sd->input_pkt_queue.lock);
+       else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
+               local_irq_disable();
+@@ -263,7 +263,7 @@ static inline void rps_lock_irq_disable(
+ static inline void rps_unlock_irq_restore(struct softnet_data *sd,
+                                         unsigned long *flags)
+ {
+-      if (IS_ENABLED(CONFIG_RPS))
++      if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
+               spin_unlock_irqrestore(&sd->input_pkt_queue.lock, *flags);
+       else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
+               local_irq_restore(*flags);
+@@ -271,7 +271,7 @@ static inline void rps_unlock_irq_restor
+ static inline void rps_unlock_irq_enable(struct softnet_data *sd)
+ {
+-      if (IS_ENABLED(CONFIG_RPS))
++      if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
+               spin_unlock_irq(&sd->input_pkt_queue.lock);
+       else if (!IS_ENABLED(CONFIG_PREEMPT_RT))
+               local_irq_enable();
+@@ -4782,6 +4782,23 @@ static void napi_schedule_rps(struct sof
+       __napi_schedule_irqoff(&mysd->backlog);
+ }
++void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu)
++{
++      unsigned long flags;
++
++      if (use_backlog_threads()) {
++              rps_lock_irqsave(sd, &flags);
++
++              if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state))
++                      __napi_schedule_irqoff(&sd->backlog);
++
++              rps_unlock_irq_restore(sd, &flags);
++
++      } else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) {
++              smp_call_function_single_async(cpu, &sd->defer_csd);
++      }
++}
++
+ #ifdef CONFIG_NET_FLOW_LIMIT
+ int netdev_flow_limit_table_len __read_mostly = (1 << 12);
+ #endif
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -6863,8 +6863,8 @@ nodefer: __kfree_skb(skb);
+       /* Make sure to trigger NET_RX_SOFTIRQ on the remote CPU
+        * if we are unlucky enough (this seems very unlikely).
+        */
+-      if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1))
+-              smp_call_function_single_async(cpu, &sd->defer_csd);
++      if (unlikely(kick))
++              kick_defer_list_purge(sd, cpu);
+ }
+ static void skb_splice_csum_page(struct sk_buff *skb, struct page *page,
diff --git a/target/linux/generic/backport-6.6/603-v6.10-net-Rename-rps_lock-to-backlog_lock.patch b/target/linux/generic/backport-6.6/603-v6.10-net-Rename-rps_lock-to-backlog_lock.patch
new file mode 100644 (file)
index 0000000..056bd56
--- /dev/null
@@ -0,0 +1,164 @@
+From 765b11f8f4e20b7433e4ba4a3e9106a0d59501ed Mon Sep 17 00:00:00 2001
+From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Date: Mon, 25 Mar 2024 08:40:31 +0100
+Subject: [PATCH 4/4] net: Rename rps_lock to backlog_lock.
+
+The rps_lock.*() functions use the inner lock of a sk_buff_head for
+locking. This lock is used if RPS is enabled, otherwise the list is
+accessed lockless and disabling interrupts is enough for the
+synchronisation because it is only accessed CPU local. Not only the list
+is protected but also the NAPI state protected.
+With the addition of backlog threads, the lock is also needed because of
+the cross CPU access even without RPS. The clean up of the defer_list
+list is also done via backlog threads (if enabled).
+
+It has been suggested to rename the locking function since it is no
+longer just RPS.
+
+Rename the rps_lock*() functions to backlog_lock*().
+
+Suggested-by: Jakub Kicinski <kuba@kernel.org>
+Acked-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ net/core/dev.c | 34 +++++++++++++++++-----------------
+ 1 file changed, 17 insertions(+), 17 deletions(-)
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -243,8 +243,8 @@ static bool use_backlog_threads(void)
+ #endif
+-static inline void rps_lock_irqsave(struct softnet_data *sd,
+-                                  unsigned long *flags)
++static inline void backlog_lock_irq_save(struct softnet_data *sd,
++                                       unsigned long *flags)
+ {
+       if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
+               spin_lock_irqsave(&sd->input_pkt_queue.lock, *flags);
+@@ -252,7 +252,7 @@ static inline void rps_lock_irqsave(stru
+               local_irq_save(*flags);
+ }
+-static inline void rps_lock_irq_disable(struct softnet_data *sd)
++static inline void backlog_lock_irq_disable(struct softnet_data *sd)
+ {
+       if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
+               spin_lock_irq(&sd->input_pkt_queue.lock);
+@@ -260,8 +260,8 @@ static inline void rps_lock_irq_disable(
+               local_irq_disable();
+ }
+-static inline void rps_unlock_irq_restore(struct softnet_data *sd,
+-                                        unsigned long *flags)
++static inline void backlog_unlock_irq_restore(struct softnet_data *sd,
++                                            unsigned long *flags)
+ {
+       if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
+               spin_unlock_irqrestore(&sd->input_pkt_queue.lock, *flags);
+@@ -269,7 +269,7 @@ static inline void rps_unlock_irq_restor
+               local_irq_restore(*flags);
+ }
+-static inline void rps_unlock_irq_enable(struct softnet_data *sd)
++static inline void backlog_unlock_irq_enable(struct softnet_data *sd)
+ {
+       if (IS_ENABLED(CONFIG_RPS) || use_backlog_threads())
+               spin_unlock_irq(&sd->input_pkt_queue.lock);
+@@ -4787,12 +4787,12 @@ void kick_defer_list_purge(struct softne
+       unsigned long flags;
+       if (use_backlog_threads()) {
+-              rps_lock_irqsave(sd, &flags);
++              backlog_lock_irq_save(sd, &flags);
+               if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state))
+                       __napi_schedule_irqoff(&sd->backlog);
+-              rps_unlock_irq_restore(sd, &flags);
++              backlog_unlock_irq_restore(sd, &flags);
+       } else if (!cmpxchg(&sd->defer_ipi_scheduled, 0, 1)) {
+               smp_call_function_single_async(cpu, &sd->defer_csd);
+@@ -4854,7 +4854,7 @@ static int enqueue_to_backlog(struct sk_
+       reason = SKB_DROP_REASON_NOT_SPECIFIED;
+       sd = &per_cpu(softnet_data, cpu);
+-      rps_lock_irqsave(sd, &flags);
++      backlog_lock_irq_save(sd, &flags);
+       if (!netif_running(skb->dev))
+               goto drop;
+       qlen = skb_queue_len(&sd->input_pkt_queue);
+@@ -4863,7 +4863,7 @@ static int enqueue_to_backlog(struct sk_
+ enqueue:
+                       __skb_queue_tail(&sd->input_pkt_queue, skb);
+                       input_queue_tail_incr_save(sd, qtail);
+-                      rps_unlock_irq_restore(sd, &flags);
++                      backlog_unlock_irq_restore(sd, &flags);
+                       return NET_RX_SUCCESS;
+               }
+@@ -4878,7 +4878,7 @@ enqueue:
+ drop:
+       sd->dropped++;
+-      rps_unlock_irq_restore(sd, &flags);
++      backlog_unlock_irq_restore(sd, &flags);
+       dev_core_stats_rx_dropped_inc(skb->dev);
+       kfree_skb_reason(skb, reason);
+@@ -5909,7 +5909,7 @@ static void flush_backlog(struct work_st
+       local_bh_disable();
+       sd = this_cpu_ptr(&softnet_data);
+-      rps_lock_irq_disable(sd);
++      backlog_lock_irq_disable(sd);
+       skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) {
+               if (skb->dev->reg_state == NETREG_UNREGISTERING) {
+                       __skb_unlink(skb, &sd->input_pkt_queue);
+@@ -5917,7 +5917,7 @@ static void flush_backlog(struct work_st
+                       input_queue_head_incr(sd);
+               }
+       }
+-      rps_unlock_irq_enable(sd);
++      backlog_unlock_irq_enable(sd);
+       skb_queue_walk_safe(&sd->process_queue, skb, tmp) {
+               if (skb->dev->reg_state == NETREG_UNREGISTERING) {
+@@ -5935,14 +5935,14 @@ static bool flush_required(int cpu)
+       struct softnet_data *sd = &per_cpu(softnet_data, cpu);
+       bool do_flush;
+-      rps_lock_irq_disable(sd);
++      backlog_lock_irq_disable(sd);
+       /* as insertion into process_queue happens with the rps lock held,
+        * process_queue access may race only with dequeue
+        */
+       do_flush = !skb_queue_empty(&sd->input_pkt_queue) ||
+                  !skb_queue_empty_lockless(&sd->process_queue);
+-      rps_unlock_irq_enable(sd);
++      backlog_unlock_irq_enable(sd);
+       return do_flush;
+ #endif
+@@ -6057,7 +6057,7 @@ static int process_backlog(struct napi_s
+               }
+-              rps_lock_irq_disable(sd);
++              backlog_lock_irq_disable(sd);
+               if (skb_queue_empty(&sd->input_pkt_queue)) {
+                       /*
+                        * Inline a custom version of __napi_complete().
+@@ -6073,7 +6073,7 @@ static int process_backlog(struct napi_s
+                       skb_queue_splice_tail_init(&sd->input_pkt_queue,
+                                                  &sd->process_queue);
+               }
+-              rps_unlock_irq_enable(sd);
++              backlog_unlock_irq_enable(sd);
+       }
+       return work;
diff --git a/target/linux/generic/backport-6.6/610-v6.9-net-mdio-add-2.5g-and-5g-related-PMA-speed-constants.patch b/target/linux/generic/backport-6.6/610-v6.9-net-mdio-add-2.5g-and-5g-related-PMA-speed-constants.patch
new file mode 100644 (file)
index 0000000..9a40ea4
--- /dev/null
@@ -0,0 +1,30 @@
+From 6c06c88fa838fcc1b7e5380facd086f57fd9d1c4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Sun, 4 Feb 2024 15:16:46 +0100
+Subject: [PATCH] net: mdio: add 2.5g and 5g related PMA speed constants
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add constants indicating 2.5g and 5g ability in the MMD PMA speed
+register.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Link: https://lore.kernel.org/r/98e15038-d96c-442f-93e4-410100d27866@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ include/uapi/linux/mdio.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/include/uapi/linux/mdio.h
++++ b/include/uapi/linux/mdio.h
+@@ -138,6 +138,8 @@
+ #define MDIO_PMA_SPEED_1000           0x0010  /* 1000M capable */
+ #define MDIO_PMA_SPEED_100            0x0020  /* 100M capable */
+ #define MDIO_PMA_SPEED_10             0x0040  /* 10M capable */
++#define MDIO_PMA_SPEED_2_5G           0x2000  /* 2.5G capable */
++#define MDIO_PMA_SPEED_5G             0x4000  /* 5G capable */
+ #define MDIO_PCS_SPEED_10P2B          0x0002  /* 10PASS-TS/2BASE-TL capable */
+ #define MDIO_PCS_SPEED_2_5G           0x0040  /* 2.5G capable */
+ #define MDIO_PCS_SPEED_5G             0x0080  /* 5G capable */
diff --git a/target/linux/generic/backport-6.6/611-01-v6.11-udp-Allow-GSO-transmit-from-devices-with-no-checksum.patch b/target/linux/generic/backport-6.6/611-01-v6.11-udp-Allow-GSO-transmit-from-devices-with-no-checksum.patch
new file mode 100644 (file)
index 0000000..2f03074
--- /dev/null
@@ -0,0 +1,94 @@
+From: Jakub Sitnicki <jakub@cloudflare.com>
+Date: Wed, 26 Jun 2024 19:51:26 +0200
+Subject: [PATCH] udp: Allow GSO transmit from devices with no checksum offload
+
+Today sending a UDP GSO packet from a TUN device results in an EIO error:
+
+  import fcntl, os, struct
+  from socket import *
+
+  TUNSETIFF = 0x400454CA
+  IFF_TUN = 0x0001
+  IFF_NO_PI = 0x1000
+  UDP_SEGMENT = 103
+
+  tun_fd = os.open("/dev/net/tun", os.O_RDWR)
+  ifr = struct.pack("16sH", b"tun0", IFF_TUN | IFF_NO_PI)
+  fcntl.ioctl(tun_fd, TUNSETIFF, ifr)
+
+  os.system("ip addr add 192.0.2.1/24 dev tun0")
+  os.system("ip link set dev tun0 up")
+
+  s = socket(AF_INET, SOCK_DGRAM)
+  s.setsockopt(SOL_UDP, UDP_SEGMENT, 1200)
+  s.sendto(b"x" * 3000, ("192.0.2.2", 9)) # EIO
+
+This is due to a check in the udp stack if the egress device offers
+checksum offload. While TUN/TAP devices, by default, don't advertise this
+capability because it requires support from the TUN/TAP reader.
+
+However, the GSO stack has a software fallback for checksum calculation,
+which we can use. This way we don't force UDP_SEGMENT users to handle the
+EIO error and implement a segmentation fallback.
+
+Lift the restriction so that UDP_SEGMENT can be used with any egress
+device. We also need to adjust the UDP GSO code to match the GSO stack
+expectation about ip_summed field, as set in commit 8d63bee643f1 ("net:
+avoid skb_warn_bad_offload false positives on UFO"). Otherwise we will hit
+the bad offload check.
+
+Users should, however, expect a potential performance impact when
+batch-sending packets with UDP_SEGMENT without checksum offload on the
+egress device. In such case the packet payload is read twice: first during
+the sendmsg syscall when copying data from user memory, and then in the GSO
+stack for checksum computation. This double memory read can be less
+efficient than a regular sendmsg where the checksum is calculated during
+the initial data copy from user memory.
+
+Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/20240626-linux-udpgso-v2-1-422dfcbd6b48@cloudflare.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/net/ipv4/udp.c
++++ b/net/ipv4/udp.c
+@@ -942,8 +942,7 @@ static int udp_send_skb(struct sk_buff *
+                       kfree_skb(skb);
+                       return -EINVAL;
+               }
+-              if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite ||
+-                  dst_xfrm(skb_dst(skb))) {
++              if (is_udplite || dst_xfrm(skb_dst(skb))) {
+                       kfree_skb(skb);
+                       return -EIO;
+               }
+--- a/net/ipv4/udp_offload.c
++++ b/net/ipv4/udp_offload.c
+@@ -380,6 +380,14 @@ struct sk_buff *__udp_gso_segment(struct
+       else
+               uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0;
++      /* On the TX path, CHECKSUM_NONE and CHECKSUM_UNNECESSARY have the same
++       * meaning. However, check for bad offloads in the GSO stack expects the
++       * latter, if the checksum was calculated in software. To vouch for the
++       * segment skbs we actually need to set it on the gso_skb.
++       */
++      if (gso_skb->ip_summed == CHECKSUM_NONE)
++              gso_skb->ip_summed = CHECKSUM_UNNECESSARY;
++
+       /* update refcount for the packet */
+       if (copy_dtor) {
+               int delta = sum_truesize - gso_skb->truesize;
+--- a/net/ipv6/udp.c
++++ b/net/ipv6/udp.c
+@@ -1261,8 +1261,7 @@ static int udp_v6_send_skb(struct sk_buf
+                       kfree_skb(skb);
+                       return -EINVAL;
+               }
+-              if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite ||
+-                  dst_xfrm(skb_dst(skb))) {
++              if (is_udplite || dst_xfrm(skb_dst(skb))) {
+                       kfree_skb(skb);
+                       return -EIO;
+               }
diff --git a/target/linux/generic/backport-6.6/611-02-v6.11-net-Make-USO-depend-on-CSUM-offload.patch b/target/linux/generic/backport-6.6/611-02-v6.11-net-Make-USO-depend-on-CSUM-offload.patch
new file mode 100644 (file)
index 0000000..42aff7f
--- /dev/null
@@ -0,0 +1,69 @@
+From: Jakub Sitnicki <jakub@cloudflare.com>
+Date: Thu, 8 Aug 2024 11:56:21 +0200
+Subject: [PATCH] net: Make USO depend on CSUM offload
+
+UDP segmentation offload inherently depends on checksum offload. It should
+not be possible to disable checksum offload while leaving USO enabled.
+Enforce this dependency in code.
+
+There is a single tx-udp-segmentation feature flag to indicate support for
+both IPv4/6, hence the devices wishing to support USO must offer checksum
+offload for both IP versions.
+
+Fixes: 10154dbded6d ("udp: Allow GSO transmit from devices with no checksum offload")
+Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
+Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://patch.msgid.link/20240808-udp-gso-egress-from-tunnel-v4-1-f5c5b4149ab9@cloudflare.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -9759,6 +9759,15 @@ static void netdev_sync_lower_features(s
+       }
+ }
++static bool netdev_has_ip_or_hw_csum(netdev_features_t features)
++{
++      netdev_features_t ip_csum_mask = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
++      bool ip_csum = (features & ip_csum_mask) == ip_csum_mask;
++      bool hw_csum = features & NETIF_F_HW_CSUM;
++
++      return ip_csum || hw_csum;
++}
++
+ static netdev_features_t netdev_fix_features(struct net_device *dev,
+       netdev_features_t features)
+ {
+@@ -9840,15 +9849,9 @@ static netdev_features_t netdev_fix_feat
+               features &= ~NETIF_F_LRO;
+       }
+-      if (features & NETIF_F_HW_TLS_TX) {
+-              bool ip_csum = (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) ==
+-                      (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
+-              bool hw_csum = features & NETIF_F_HW_CSUM;
+-
+-              if (!ip_csum && !hw_csum) {
+-                      netdev_dbg(dev, "Dropping TLS TX HW offload feature since no CSUM feature.\n");
+-                      features &= ~NETIF_F_HW_TLS_TX;
+-              }
++      if ((features & NETIF_F_HW_TLS_TX) && !netdev_has_ip_or_hw_csum(features)) {
++              netdev_dbg(dev, "Dropping TLS TX HW offload feature since no CSUM feature.\n");
++              features &= ~NETIF_F_HW_TLS_TX;
+       }
+       if ((features & NETIF_F_HW_TLS_RX) && !(features & NETIF_F_RXCSUM)) {
+@@ -9856,6 +9859,11 @@ static netdev_features_t netdev_fix_feat
+               features &= ~NETIF_F_HW_TLS_RX;
+       }
++      if ((features & NETIF_F_GSO_UDP_L4) && !netdev_has_ip_or_hw_csum(features)) {
++              netdev_dbg(dev, "Dropping USO feature since no CSUM feature.\n");
++              features &= ~NETIF_F_GSO_UDP_L4;
++      }
++
+       return features;
+ }
diff --git a/target/linux/generic/backport-6.6/611-03-v6.11-udp-Fall-back-to-software-USO-if-IPv6-extension-head.patch b/target/linux/generic/backport-6.6/611-03-v6.11-udp-Fall-back-to-software-USO-if-IPv6-extension-head.patch
new file mode 100644 (file)
index 0000000..e8eceb8
--- /dev/null
@@ -0,0 +1,86 @@
+From: Jakub Sitnicki <jakub@cloudflare.com>
+Date: Thu, 8 Aug 2024 11:56:22 +0200
+Subject: [PATCH] udp: Fall back to software USO if IPv6 extension headers are
+ present
+
+In commit 10154dbded6d ("udp: Allow GSO transmit from devices with no
+checksum offload") we have intentionally allowed UDP GSO packets marked
+CHECKSUM_NONE to pass to the GSO stack, so that they can be segmented and
+checksummed by a software fallback when the egress device lacks these
+features.
+
+What was not taken into consideration is that a CHECKSUM_NONE skb can be
+handed over to the GSO stack also when the egress device advertises the
+tx-udp-segmentation / NETIF_F_GSO_UDP_L4 feature.
+
+This will happen when there are IPv6 extension headers present, which we
+check for in __ip6_append_data(). Syzbot has discovered this scenario,
+producing a warning as below:
+
+  ip6tnl0: caps=(0x00000006401d7869, 0x00000006401d7869)
+  WARNING: CPU: 0 PID: 5112 at net/core/dev.c:3293 skb_warn_bad_offload+0x166/0x1a0 net/core/dev.c:3291
+  Modules linked in:
+  CPU: 0 PID: 5112 Comm: syz-executor391 Not tainted 6.10.0-rc7-syzkaller-01603-g80ab5445da62 #0
+  Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 06/07/2024
+  RIP: 0010:skb_warn_bad_offload+0x166/0x1a0 net/core/dev.c:3291
+  [...]
+  Call Trace:
+   <TASK>
+   __skb_gso_segment+0x3be/0x4c0 net/core/gso.c:127
+   skb_gso_segment include/net/gso.h:83 [inline]
+   validate_xmit_skb+0x585/0x1120 net/core/dev.c:3661
+   __dev_queue_xmit+0x17a4/0x3e90 net/core/dev.c:4415
+   neigh_output include/net/neighbour.h:542 [inline]
+   ip6_finish_output2+0xffa/0x1680 net/ipv6/ip6_output.c:137
+   ip6_finish_output+0x41e/0x810 net/ipv6/ip6_output.c:222
+   ip6_send_skb+0x112/0x230 net/ipv6/ip6_output.c:1958
+   udp_v6_send_skb+0xbf5/0x1870 net/ipv6/udp.c:1292
+   udpv6_sendmsg+0x23b3/0x3270 net/ipv6/udp.c:1588
+   sock_sendmsg_nosec net/socket.c:730 [inline]
+   __sock_sendmsg+0xef/0x270 net/socket.c:745
+   ____sys_sendmsg+0x525/0x7d0 net/socket.c:2585
+   ___sys_sendmsg net/socket.c:2639 [inline]
+   __sys_sendmmsg+0x3b2/0x740 net/socket.c:2725
+   __do_sys_sendmmsg net/socket.c:2754 [inline]
+   __se_sys_sendmmsg net/socket.c:2751 [inline]
+   __x64_sys_sendmmsg+0xa0/0xb0 net/socket.c:2751
+   do_syscall_x64 arch/x86/entry/common.c:52 [inline]
+   do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
+   entry_SYSCALL_64_after_hwframe+0x77/0x7f
+   [...]
+   </TASK>
+
+We are hitting the bad offload warning because when an egress device is
+capable of handling segmentation offload requested by
+skb_shinfo(skb)->gso_type, the chain of gso_segment callbacks won't produce
+any segment skbs and return NULL. See the skb_gso_ok() branch in
+{__udp,tcp,sctp}_gso_segment helpers.
+
+To fix it, force a fallback to software USO when processing a packet with
+IPv6 extension headers, since we don't know if these can checksummed by
+all devices which offer USO.
+
+Fixes: 10154dbded6d ("udp: Allow GSO transmit from devices with no checksum offload")
+Reported-by: syzbot+e15b7e15b8a751a91d9a@syzkaller.appspotmail.com
+Closes: https://lore.kernel.org/all/000000000000e1609a061d5330ce@google.com/
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
+Link: https://patch.msgid.link/20240808-udp-gso-egress-from-tunnel-v4-2-f5c5b4149ab9@cloudflare.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+
+--- a/net/ipv4/udp_offload.c
++++ b/net/ipv4/udp_offload.c
+@@ -283,6 +283,12 @@ struct sk_buff *__udp_gso_segment(struct
+                    !(skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST)))
+               return ERR_PTR(-EINVAL);
++      /* We don't know if egress device can segment and checksum the packet
++       * when IPv6 extension headers are present. Fall back to software GSO.
++       */
++      if (gso_skb->ip_summed != CHECKSUM_PARTIAL)
++              features &= ~(NETIF_F_GSO_UDP_L4 | NETIF_F_CSUM_MASK);
++
+       if (skb_gso_ok(gso_skb, features | NETIF_F_GSO_ROBUST)) {
+               /* Packet is from an untrusted source, reset gso_segs. */
+               skb_shinfo(gso_skb)->gso_segs = DIV_ROUND_UP(gso_skb->len - sizeof(*uh),
diff --git a/target/linux/generic/backport-6.6/612-v6.9-net-get-stats64-if-device-if-driver-is-configured.patch b/target/linux/generic/backport-6.6/612-v6.9-net-get-stats64-if-device-if-driver-is-configured.patch
new file mode 100644 (file)
index 0000000..d72882d
--- /dev/null
@@ -0,0 +1,29 @@
+From: Breno Leitao <leitao@debian.org>
+Date: Wed, 28 Feb 2024 03:31:21 -0800
+Subject: [PATCH] net: get stats64 if device if driver is configured
+
+If the network driver is relying in the net core to do stats allocation,
+then we want to dev_get_tstats64() instead of netdev_stats_to_stats64(),
+since there are per-cpu stats that needs to be taken in consideration.
+
+This will also simplify the drivers in regard to statistics. Once the
+driver sets NETDEV_PCPU_STAT_TSTATS, it doesn't not need to allocate the
+stacks, neither it needs to set `.ndo_get_stats64 = dev_get_tstats64`
+for the generic stats collection function anymore.
+
+Signed-off-by: Breno Leitao <leitao@debian.org>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -10666,6 +10666,8 @@ struct rtnl_link_stats64 *dev_get_stats(
+               ops->ndo_get_stats64(dev, storage);
+       } else if (ops->ndo_get_stats) {
+               netdev_stats_to_stats64(storage, ops->ndo_get_stats(dev));
++      } else if (dev->pcpu_stat_type == NETDEV_PCPU_STAT_TSTATS) {
++              dev_get_tstats64(dev, storage);
+       } else {
+               netdev_stats_to_stats64(storage, &dev->stats);
+       }
diff --git a/target/linux/generic/backport-6.6/700-v6.10-net-create-a-dummy-net_device-allocator.patch b/target/linux/generic/backport-6.6/700-v6.10-net-create-a-dummy-net_device-allocator.patch
new file mode 100644 (file)
index 0000000..3f48a8c
--- /dev/null
@@ -0,0 +1,135 @@
+From c661050f93d3fd37a33c06041bb18a89688de7d2 Mon Sep 17 00:00:00 2001
+From: Breno Leitao <leitao@debian.org>
+Date: Mon, 22 Apr 2024 05:38:56 -0700
+Subject: [PATCH] net: create a dummy net_device allocator
+
+It is impossible to use init_dummy_netdev together with alloc_netdev()
+as the 'setup' argument.
+
+This is because alloc_netdev() initializes some fields in the net_device
+structure, and later init_dummy_netdev() memzero them all. This causes
+some problems as reported here:
+
+       https://lore.kernel.org/all/20240322082336.49f110cc@kernel.org/
+
+Split the init_dummy_netdev() function in two. Create a new function called
+init_dummy_netdev_core() that does not memzero the net_device structure.
+Then have init_dummy_netdev() memzero-ing and calling
+init_dummy_netdev_core(), keeping the old behaviour.
+
+init_dummy_netdev_core() is the new function that could be called as an
+argument for alloc_netdev().
+
+Also, create a helper to allocate and initialize dummy net devices,
+leveraging init_dummy_netdev_core() as the setup argument. This function
+basically simplify the allocation of dummy devices, by allocating and
+initializing it. Freeing the device continue to be done through
+free_netdev()
+
+Suggested-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Breno Leitao <leitao@debian.org>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/netdevice.h |  3 +++
+ net/core/dev.c            | 56 ++++++++++++++++++++++++++-------------
+ 2 files changed, 41 insertions(+), 18 deletions(-)
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -4561,6 +4561,9 @@ static inline void netif_addr_unlock_bh(
+ void ether_setup(struct net_device *dev);
++/* Allocate dummy net_device */
++struct net_device *alloc_netdev_dummy(int sizeof_priv);
++
+ /* Support for loadable net-drivers */
+ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
+                                   unsigned char name_assign_type,
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -10353,25 +10353,12 @@ err_free_name:
+ }
+ EXPORT_SYMBOL(register_netdevice);
+-/**
+- *    init_dummy_netdev       - init a dummy network device for NAPI
+- *    @dev: device to init
+- *
+- *    This takes a network device structure and initialize the minimum
+- *    amount of fields so it can be used to schedule NAPI polls without
+- *    registering a full blown interface. This is to be used by drivers
+- *    that need to tie several hardware interfaces to a single NAPI
+- *    poll scheduler due to HW limitations.
++/* Initialize the core of a dummy net device.
++ * This is useful if you are calling this function after alloc_netdev(),
++ * since it does not memset the net_device fields.
+  */
+-int init_dummy_netdev(struct net_device *dev)
++static void init_dummy_netdev_core(struct net_device *dev)
+ {
+-      /* Clear everything. Note we don't initialize spinlocks
+-       * are they aren't supposed to be taken by any of the
+-       * NAPI code and this dummy netdev is supposed to be
+-       * only ever used for NAPI polls
+-       */
+-      memset(dev, 0, sizeof(struct net_device));
+-
+       /* make sure we BUG if trying to hit standard
+        * register/unregister code path
+        */
+@@ -10391,12 +10378,32 @@ int init_dummy_netdev(struct net_device
+        * because users of this 'device' dont need to change
+        * its refcount.
+        */
++}
++
++/**
++ *    init_dummy_netdev       - init a dummy network device for NAPI
++ *    @dev: device to init
++ *
++ *    This takes a network device structure and initializes the minimum
++ *    amount of fields so it can be used to schedule NAPI polls without
++ *    registering a full blown interface. This is to be used by drivers
++ *    that need to tie several hardware interfaces to a single NAPI
++ *    poll scheduler due to HW limitations.
++ */
++int init_dummy_netdev(struct net_device *dev)
++{
++      /* Clear everything. Note we don't initialize spinlocks
++       * as they aren't supposed to be taken by any of the
++       * NAPI code and this dummy netdev is supposed to be
++       * only ever used for NAPI polls
++       */
++      memset(dev, 0, sizeof(struct net_device));
++      init_dummy_netdev_core(dev);
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(init_dummy_netdev);
+-
+ /**
+  *    register_netdev - register a network device
+  *    @dev: device to register
+@@ -10990,6 +10997,19 @@ void free_netdev(struct net_device *dev)
+ EXPORT_SYMBOL(free_netdev);
+ /**
++ * alloc_netdev_dummy - Allocate and initialize a dummy net device.
++ * @sizeof_priv: size of private data to allocate space for
++ *
++ * Return: the allocated net_device on success, NULL otherwise
++ */
++struct net_device *alloc_netdev_dummy(int sizeof_priv)
++{
++      return alloc_netdev(sizeof_priv, "dummy#", NET_NAME_UNKNOWN,
++                          init_dummy_netdev_core);
++}
++EXPORT_SYMBOL_GPL(alloc_netdev_dummy);
++
++/**
+  *    synchronize_net -  Synchronize with packet receive processing
+  *
+  *    Wait for packets currently being received to be done.
diff --git a/target/linux/generic/backport-6.6/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch b/target/linux/generic/backport-6.6/702-01-v6.7-net-phy-aquantia-move-to-separate-directory.patch
new file mode 100644 (file)
index 0000000..be4d4cc
--- /dev/null
@@ -0,0 +1,2386 @@
+From d2213db3f49bce8e7a87c8de05b9a091f78f654e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 14 Nov 2023 15:08:41 +0100
+Subject: [PATCH 1/3] net: phy: aquantia: move to separate directory
+
+Move aquantia PHY driver to separate driectory in preparation for
+firmware loading support to keep things tidy.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/Kconfig                         | 5 +----
+ drivers/net/phy/Makefile                        | 6 +-----
+ drivers/net/phy/aquantia/Kconfig                | 5 +++++
+ drivers/net/phy/aquantia/Makefile               | 6 ++++++
+ drivers/net/phy/{ => aquantia}/aquantia.h       | 0
+ drivers/net/phy/{ => aquantia}/aquantia_hwmon.c | 0
+ drivers/net/phy/{ => aquantia}/aquantia_main.c  | 0
+ 7 files changed, 13 insertions(+), 9 deletions(-)
+ create mode 100644 drivers/net/phy/aquantia/Kconfig
+ create mode 100644 drivers/net/phy/aquantia/Makefile
+ rename drivers/net/phy/{ => aquantia}/aquantia.h (100%)
+ rename drivers/net/phy/{ => aquantia}/aquantia_hwmon.c (100%)
+ rename drivers/net/phy/{ => aquantia}/aquantia_main.c (100%)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -96,10 +96,7 @@ config ADIN1100_PHY
+         Currently supports the:
+         - ADIN1100 - Robust,Industrial, Low Power 10BASE-T1L Ethernet PHY
+-config AQUANTIA_PHY
+-      tristate "Aquantia PHYs"
+-      help
+-        Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
++source "drivers/net/phy/aquantia/Kconfig"
+ config AX88796B_PHY
+       tristate "Asix PHYs"
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -35,11 +35,7 @@ obj-y                               += $(sfp-obj-y) $(sfp-obj-m)
+ obj-$(CONFIG_ADIN_PHY)                += adin.o
+ obj-$(CONFIG_ADIN1100_PHY)    += adin1100.o
+ obj-$(CONFIG_AMD_PHY)         += amd.o
+-aquantia-objs                 += aquantia_main.o
+-ifdef CONFIG_HWMON
+-aquantia-objs                 += aquantia_hwmon.o
+-endif
+-obj-$(CONFIG_AQUANTIA_PHY)    += aquantia.o
++obj-$(CONFIG_AQUANTIA_PHY)    += aquantia/
+ obj-$(CONFIG_AT803X_PHY)      += at803x.o
+ obj-$(CONFIG_AX88796B_PHY)    += ax88796b.o
+ obj-$(CONFIG_BCM54140_PHY)    += bcm54140.o
+--- /dev/null
++++ b/drivers/net/phy/aquantia/Kconfig
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0-only
++config AQUANTIA_PHY
++      tristate "Aquantia PHYs"
++      help
++        Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
+--- /dev/null
++++ b/drivers/net/phy/aquantia/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++aquantia-objs                 += aquantia_main.o
++ifdef CONFIG_HWMON
++aquantia-objs                 += aquantia_hwmon.o
++endif
++obj-$(CONFIG_AQUANTIA_PHY)    += aquantia.o
+--- a/drivers/net/phy/aquantia.h
++++ /dev/null
+@@ -1,16 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-/* HWMON driver for Aquantia PHY
+- *
+- * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
+- * Author: Andrew Lunn <andrew@lunn.ch>
+- * Author: Heiner Kallweit <hkallweit1@gmail.com>
+- */
+-
+-#include <linux/device.h>
+-#include <linux/phy.h>
+-
+-#if IS_REACHABLE(CONFIG_HWMON)
+-int aqr_hwmon_probe(struct phy_device *phydev);
+-#else
+-static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; }
+-#endif
+--- /dev/null
++++ b/drivers/net/phy/aquantia/aquantia.h
+@@ -0,0 +1,16 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/* HWMON driver for Aquantia PHY
++ *
++ * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
++ * Author: Andrew Lunn <andrew@lunn.ch>
++ * Author: Heiner Kallweit <hkallweit1@gmail.com>
++ */
++
++#include <linux/device.h>
++#include <linux/phy.h>
++
++#if IS_REACHABLE(CONFIG_HWMON)
++int aqr_hwmon_probe(struct phy_device *phydev);
++#else
++static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; }
++#endif
+--- /dev/null
++++ b/drivers/net/phy/aquantia/aquantia_hwmon.c
+@@ -0,0 +1,250 @@
++// SPDX-License-Identifier: GPL-2.0
++/* HWMON driver for Aquantia PHY
++ *
++ * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
++ * Author: Andrew Lunn <andrew@lunn.ch>
++ * Author: Heiner Kallweit <hkallweit1@gmail.com>
++ */
++
++#include <linux/phy.h>
++#include <linux/device.h>
++#include <linux/ctype.h>
++#include <linux/hwmon.h>
++
++#include "aquantia.h"
++
++/* Vendor specific 1, MDIO_MMD_VEND2 */
++#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL     0xc421
++#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL      0xc422
++#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN     0xc423
++#define VEND1_THERMAL_PROV_LOW_TEMP_WARN      0xc424
++#define VEND1_THERMAL_STAT1                   0xc820
++#define VEND1_THERMAL_STAT2                   0xc821
++#define VEND1_THERMAL_STAT2_VALID             BIT(0)
++#define VEND1_GENERAL_STAT1                   0xc830
++#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL    BIT(14)
++#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL     BIT(13)
++#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN    BIT(12)
++#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN     BIT(11)
++
++#if IS_REACHABLE(CONFIG_HWMON)
++
++static umode_t aqr_hwmon_is_visible(const void *data,
++                                  enum hwmon_sensor_types type,
++                                  u32 attr, int channel)
++{
++      if (type != hwmon_temp)
++              return 0;
++
++      switch (attr) {
++      case hwmon_temp_input:
++      case hwmon_temp_min_alarm:
++      case hwmon_temp_max_alarm:
++      case hwmon_temp_lcrit_alarm:
++      case hwmon_temp_crit_alarm:
++              return 0444;
++      case hwmon_temp_min:
++      case hwmon_temp_max:
++      case hwmon_temp_lcrit:
++      case hwmon_temp_crit:
++              return 0644;
++      default:
++              return 0;
++      }
++}
++
++static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value)
++{
++      int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg);
++
++      if (temp < 0)
++              return temp;
++
++      /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */
++      *value = (s16)temp * 1000 / 256;
++
++      return 0;
++}
++
++static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value)
++{
++      int temp;
++
++      if (value >= 128000 || value < -128000)
++              return -ERANGE;
++
++      temp = value * 256 / 1000;
++
++      /* temp is in s16 range and we're interested in lower 16 bits only */
++      return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, (u16)temp);
++}
++
++static int aqr_hwmon_test_bit(struct phy_device *phydev, int reg, int bit)
++{
++      int val = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg);
++
++      if (val < 0)
++              return val;
++
++      return !!(val & bit);
++}
++
++static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value)
++{
++      int val = aqr_hwmon_test_bit(phydev, VEND1_GENERAL_STAT1, bit);
++
++      if (val < 0)
++              return val;
++
++      *value = val;
++
++      return 0;
++}
++
++static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
++                        u32 attr, int channel, long *value)
++{
++      struct phy_device *phydev = dev_get_drvdata(dev);
++      int reg;
++
++      if (type != hwmon_temp)
++              return -EOPNOTSUPP;
++
++      switch (attr) {
++      case hwmon_temp_input:
++              reg = aqr_hwmon_test_bit(phydev, VEND1_THERMAL_STAT2,
++                                       VEND1_THERMAL_STAT2_VALID);
++              if (reg < 0)
++                      return reg;
++              if (!reg)
++                      return -EBUSY;
++
++              return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value);
++
++      case hwmon_temp_lcrit:
++              return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL,
++                                   value);
++      case hwmon_temp_min:
++              return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN,
++                                   value);
++      case hwmon_temp_max:
++              return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN,
++                                   value);
++      case hwmon_temp_crit:
++              return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL,
++                                   value);
++      case hwmon_temp_lcrit_alarm:
++              return aqr_hwmon_status1(phydev,
++                                       VEND1_GENERAL_STAT1_LOW_TEMP_FAIL,
++                                       value);
++      case hwmon_temp_min_alarm:
++              return aqr_hwmon_status1(phydev,
++                                       VEND1_GENERAL_STAT1_LOW_TEMP_WARN,
++                                       value);
++      case hwmon_temp_max_alarm:
++              return aqr_hwmon_status1(phydev,
++                                       VEND1_GENERAL_STAT1_HIGH_TEMP_WARN,
++                                       value);
++      case hwmon_temp_crit_alarm:
++              return aqr_hwmon_status1(phydev,
++                                       VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL,
++                                       value);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
++                         u32 attr, int channel, long value)
++{
++      struct phy_device *phydev = dev_get_drvdata(dev);
++
++      if (type != hwmon_temp)
++              return -EOPNOTSUPP;
++
++      switch (attr) {
++      case hwmon_temp_lcrit:
++              return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL,
++                                   value);
++      case hwmon_temp_min:
++              return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN,
++                                   value);
++      case hwmon_temp_max:
++              return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN,
++                                   value);
++      case hwmon_temp_crit:
++              return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL,
++                                   value);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++static const struct hwmon_ops aqr_hwmon_ops = {
++      .is_visible = aqr_hwmon_is_visible,
++      .read = aqr_hwmon_read,
++      .write = aqr_hwmon_write,
++};
++
++static u32 aqr_hwmon_chip_config[] = {
++      HWMON_C_REGISTER_TZ,
++      0,
++};
++
++static const struct hwmon_channel_info aqr_hwmon_chip = {
++      .type = hwmon_chip,
++      .config = aqr_hwmon_chip_config,
++};
++
++static u32 aqr_hwmon_temp_config[] = {
++      HWMON_T_INPUT |
++      HWMON_T_MAX | HWMON_T_MIN |
++      HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM |
++      HWMON_T_CRIT | HWMON_T_LCRIT |
++      HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM,
++      0,
++};
++
++static const struct hwmon_channel_info aqr_hwmon_temp = {
++      .type = hwmon_temp,
++      .config = aqr_hwmon_temp_config,
++};
++
++static const struct hwmon_channel_info * const aqr_hwmon_info[] = {
++      &aqr_hwmon_chip,
++      &aqr_hwmon_temp,
++      NULL,
++};
++
++static const struct hwmon_chip_info aqr_hwmon_chip_info = {
++      .ops = &aqr_hwmon_ops,
++      .info = aqr_hwmon_info,
++};
++
++int aqr_hwmon_probe(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      struct device *hwmon_dev;
++      char *hwmon_name;
++      int i, j;
++
++      hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
++      if (!hwmon_name)
++              return -ENOMEM;
++
++      for (i = j = 0; hwmon_name[i]; i++) {
++              if (isalnum(hwmon_name[i])) {
++                      if (i != j)
++                              hwmon_name[j] = hwmon_name[i];
++                      j++;
++              }
++      }
++      hwmon_name[j] = '\0';
++
++      hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name,
++                                      phydev, &aqr_hwmon_chip_info, NULL);
++
++      return PTR_ERR_OR_ZERO(hwmon_dev);
++}
++
++#endif
+--- /dev/null
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -0,0 +1,882 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Driver for Aquantia PHY
++ *
++ * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
++ *
++ * Copyright 2015 Freescale Semiconductor, Inc.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/bitfield.h>
++#include <linux/phy.h>
++
++#include "aquantia.h"
++
++#define PHY_ID_AQ1202 0x03a1b445
++#define PHY_ID_AQ2104 0x03a1b460
++#define PHY_ID_AQR105 0x03a1b4a2
++#define PHY_ID_AQR106 0x03a1b4d0
++#define PHY_ID_AQR107 0x03a1b4e0
++#define PHY_ID_AQCS109        0x03a1b5c2
++#define PHY_ID_AQR405 0x03a1b4b0
++#define PHY_ID_AQR112 0x03a1b662
++#define PHY_ID_AQR412 0x03a1b712
++#define PHY_ID_AQR113C        0x31c31c12
++
++#define MDIO_PHYXS_VEND_IF_STATUS             0xe812
++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK   GENMASK(7, 3)
++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR     0
++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX     1
++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI    2
++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII        3
++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI   4
++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII  6
++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI  7
++#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII        10
++
++#define MDIO_AN_VEND_PROV                     0xc400
++#define MDIO_AN_VEND_PROV_1000BASET_FULL      BIT(15)
++#define MDIO_AN_VEND_PROV_1000BASET_HALF      BIT(14)
++#define MDIO_AN_VEND_PROV_5000BASET_FULL      BIT(11)
++#define MDIO_AN_VEND_PROV_2500BASET_FULL      BIT(10)
++#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN                BIT(4)
++#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK      GENMASK(3, 0)
++#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT      4
++
++#define MDIO_AN_TX_VEND_STATUS1                       0xc800
++#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK     GENMASK(3, 1)
++#define MDIO_AN_TX_VEND_STATUS1_10BASET               0
++#define MDIO_AN_TX_VEND_STATUS1_100BASETX     1
++#define MDIO_AN_TX_VEND_STATUS1_1000BASET     2
++#define MDIO_AN_TX_VEND_STATUS1_10GBASET      3
++#define MDIO_AN_TX_VEND_STATUS1_2500BASET     4
++#define MDIO_AN_TX_VEND_STATUS1_5000BASET     5
++#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX   BIT(0)
++
++#define MDIO_AN_TX_VEND_INT_STATUS1           0xcc00
++#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1)
++
++#define MDIO_AN_TX_VEND_INT_STATUS2           0xcc01
++#define MDIO_AN_TX_VEND_INT_STATUS2_MASK      BIT(0)
++
++#define MDIO_AN_TX_VEND_INT_MASK2             0xd401
++#define MDIO_AN_TX_VEND_INT_MASK2_LINK                BIT(0)
++
++#define MDIO_AN_RX_LP_STAT1                   0xe820
++#define MDIO_AN_RX_LP_STAT1_1000BASET_FULL    BIT(15)
++#define MDIO_AN_RX_LP_STAT1_1000BASET_HALF    BIT(14)
++#define MDIO_AN_RX_LP_STAT1_SHORT_REACH               BIT(13)
++#define MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT  BIT(12)
++#define MDIO_AN_RX_LP_STAT1_AQ_PHY            BIT(2)
++
++#define MDIO_AN_RX_LP_STAT4                   0xe823
++#define MDIO_AN_RX_LP_STAT4_FW_MAJOR          GENMASK(15, 8)
++#define MDIO_AN_RX_LP_STAT4_FW_MINOR          GENMASK(7, 0)
++
++#define MDIO_AN_RX_VEND_STAT3                 0xe832
++#define MDIO_AN_RX_VEND_STAT3_AFR             BIT(0)
++
++/* MDIO_MMD_C22EXT */
++#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES         0xd292
++#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES          0xd294
++#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER               0xd297
++#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES         0xd313
++#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES          0xd315
++#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER               0xd317
++#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS          0xd318
++#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS     0xd319
++#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR     0xd31a
++#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES         0xd31b
++
++/* Vendor specific 1, MDIO_MMD_VEND1 */
++#define VEND1_GLOBAL_FW_ID                    0x0020
++#define VEND1_GLOBAL_FW_ID_MAJOR              GENMASK(15, 8)
++#define VEND1_GLOBAL_FW_ID_MINOR              GENMASK(7, 0)
++
++#define VEND1_GLOBAL_GEN_STAT2                        0xc831
++#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG     BIT(15)
++
++/* The following registers all have similar layouts; first the registers... */
++#define VEND1_GLOBAL_CFG_10M                  0x0310
++#define VEND1_GLOBAL_CFG_100M                 0x031b
++#define VEND1_GLOBAL_CFG_1G                   0x031c
++#define VEND1_GLOBAL_CFG_2_5G                 0x031d
++#define VEND1_GLOBAL_CFG_5G                   0x031e
++#define VEND1_GLOBAL_CFG_10G                  0x031f
++/* ...and now the fields */
++#define VEND1_GLOBAL_CFG_RATE_ADAPT           GENMASK(8, 7)
++#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE      0
++#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX               1
++#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE     2
++
++#define VEND1_GLOBAL_RSVD_STAT1                       0xc885
++#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID   GENMASK(7, 4)
++#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID               GENMASK(3, 0)
++
++#define VEND1_GLOBAL_RSVD_STAT9                       0xc88d
++#define VEND1_GLOBAL_RSVD_STAT9_MODE          GENMASK(7, 0)
++#define VEND1_GLOBAL_RSVD_STAT9_1000BT2               0x23
++
++#define VEND1_GLOBAL_INT_STD_STATUS           0xfc00
++#define VEND1_GLOBAL_INT_VEND_STATUS          0xfc01
++
++#define VEND1_GLOBAL_INT_STD_MASK             0xff00
++#define VEND1_GLOBAL_INT_STD_MASK_PMA1                BIT(15)
++#define VEND1_GLOBAL_INT_STD_MASK_PMA2                BIT(14)
++#define VEND1_GLOBAL_INT_STD_MASK_PCS1                BIT(13)
++#define VEND1_GLOBAL_INT_STD_MASK_PCS2                BIT(12)
++#define VEND1_GLOBAL_INT_STD_MASK_PCS3                BIT(11)
++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1     BIT(10)
++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2     BIT(9)
++#define VEND1_GLOBAL_INT_STD_MASK_AN1         BIT(8)
++#define VEND1_GLOBAL_INT_STD_MASK_AN2         BIT(7)
++#define VEND1_GLOBAL_INT_STD_MASK_GBE         BIT(6)
++#define VEND1_GLOBAL_INT_STD_MASK_ALL         BIT(0)
++
++#define VEND1_GLOBAL_INT_VEND_MASK            0xff01
++#define VEND1_GLOBAL_INT_VEND_MASK_PMA                BIT(15)
++#define VEND1_GLOBAL_INT_VEND_MASK_PCS                BIT(14)
++#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS     BIT(13)
++#define VEND1_GLOBAL_INT_VEND_MASK_AN         BIT(12)
++#define VEND1_GLOBAL_INT_VEND_MASK_GBE                BIT(11)
++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1    BIT(2)
++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2    BIT(1)
++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3    BIT(0)
++
++/* Sleep and timeout for checking if the Processor-Intensive
++ * MDIO operation is finished
++ */
++#define AQR107_OP_IN_PROG_SLEEP               1000
++#define AQR107_OP_IN_PROG_TIMEOUT     100000
++
++struct aqr107_hw_stat {
++      const char *name;
++      int reg;
++      int size;
++};
++
++#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s }
++static const struct aqr107_hw_stat aqr107_hw_stats[] = {
++      SGMII_STAT("sgmii_rx_good_frames",          RX_GOOD_FRAMES,     26),
++      SGMII_STAT("sgmii_rx_bad_frames",           RX_BAD_FRAMES,      26),
++      SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER,    8),
++      SGMII_STAT("sgmii_tx_good_frames",          TX_GOOD_FRAMES,     26),
++      SGMII_STAT("sgmii_tx_bad_frames",           TX_BAD_FRAMES,      26),
++      SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER,    8),
++      SGMII_STAT("sgmii_tx_collisions",           TX_COLLISIONS,       8),
++      SGMII_STAT("sgmii_tx_line_collisions",      TX_LINE_COLLISIONS,  8),
++      SGMII_STAT("sgmii_tx_frame_alignment_err",  TX_FRAME_ALIGN_ERR, 16),
++      SGMII_STAT("sgmii_tx_runt_frames",          TX_RUNT_FRAMES,     22),
++};
++#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
++
++struct aqr107_priv {
++      u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
++};
++
++static int aqr107_get_sset_count(struct phy_device *phydev)
++{
++      return AQR107_SGMII_STAT_SZ;
++}
++
++static void aqr107_get_strings(struct phy_device *phydev, u8 *data)
++{
++      int i;
++
++      for (i = 0; i < AQR107_SGMII_STAT_SZ; i++)
++              strscpy(data + i * ETH_GSTRING_LEN, aqr107_hw_stats[i].name,
++                      ETH_GSTRING_LEN);
++}
++
++static u64 aqr107_get_stat(struct phy_device *phydev, int index)
++{
++      const struct aqr107_hw_stat *stat = aqr107_hw_stats + index;
++      int len_l = min(stat->size, 16);
++      int len_h = stat->size - len_l;
++      u64 ret;
++      int val;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg);
++      if (val < 0)
++              return U64_MAX;
++
++      ret = val & GENMASK(len_l - 1, 0);
++      if (len_h) {
++              val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg + 1);
++              if (val < 0)
++                      return U64_MAX;
++
++              ret += (val & GENMASK(len_h - 1, 0)) << 16;
++      }
++
++      return ret;
++}
++
++static void aqr107_get_stats(struct phy_device *phydev,
++                           struct ethtool_stats *stats, u64 *data)
++{
++      struct aqr107_priv *priv = phydev->priv;
++      u64 val;
++      int i;
++
++      for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) {
++              val = aqr107_get_stat(phydev, i);
++              if (val == U64_MAX)
++                      phydev_err(phydev, "Reading HW Statistics failed for %s\n",
++                                 aqr107_hw_stats[i].name);
++              else
++                      priv->sgmii_stats[i] += val;
++
++              data[i] = priv->sgmii_stats[i];
++      }
++}
++
++static int aqr_config_aneg(struct phy_device *phydev)
++{
++      bool changed = false;
++      u16 reg;
++      int ret;
++
++      if (phydev->autoneg == AUTONEG_DISABLE)
++              return genphy_c45_pma_setup_forced(phydev);
++
++      ret = genphy_c45_an_config_aneg(phydev);
++      if (ret < 0)
++              return ret;
++      if (ret > 0)
++              changed = true;
++
++      /* Clause 45 has no standardized support for 1000BaseT, therefore
++       * use vendor registers for this mode.
++       */
++      reg = 0;
++      if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
++                            phydev->advertising))
++              reg |= MDIO_AN_VEND_PROV_1000BASET_FULL;
++
++      if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
++                            phydev->advertising))
++              reg |= MDIO_AN_VEND_PROV_1000BASET_HALF;
++
++      /* Handle the case when the 2.5G and 5G speeds are not advertised */
++      if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++                            phydev->advertising))
++              reg |= MDIO_AN_VEND_PROV_2500BASET_FULL;
++
++      if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
++                            phydev->advertising))
++              reg |= MDIO_AN_VEND_PROV_5000BASET_FULL;
++
++      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV,
++                                   MDIO_AN_VEND_PROV_1000BASET_HALF |
++                                   MDIO_AN_VEND_PROV_1000BASET_FULL |
++                                   MDIO_AN_VEND_PROV_2500BASET_FULL |
++                                   MDIO_AN_VEND_PROV_5000BASET_FULL, reg);
++      if (ret < 0)
++              return ret;
++      if (ret > 0)
++              changed = true;
++
++      return genphy_c45_check_and_restart_aneg(phydev, changed);
++}
++
++static int aqr_config_intr(struct phy_device *phydev)
++{
++      bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED;
++      int err;
++
++      if (en) {
++              /* Clear any pending interrupts before enabling them */
++              err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2);
++              if (err < 0)
++                      return err;
++      }
++
++      err = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_MASK2,
++                          en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0);
++      if (err < 0)
++              return err;
++
++      err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK,
++                          en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0);
++      if (err < 0)
++              return err;
++
++      err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK,
++                          en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 |
++                          VEND1_GLOBAL_INT_VEND_MASK_AN : 0);
++      if (err < 0)
++              return err;
++
++      if (!en) {
++              /* Clear any pending interrupts after we have disabled them */
++              err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2);
++              if (err < 0)
++                      return err;
++      }
++
++      return 0;
++}
++
++static irqreturn_t aqr_handle_interrupt(struct phy_device *phydev)
++{
++      int irq_status;
++
++      irq_status = phy_read_mmd(phydev, MDIO_MMD_AN,
++                                MDIO_AN_TX_VEND_INT_STATUS2);
++      if (irq_status < 0) {
++              phy_error(phydev);
++              return IRQ_NONE;
++      }
++
++      if (!(irq_status & MDIO_AN_TX_VEND_INT_STATUS2_MASK))
++              return IRQ_NONE;
++
++      phy_trigger_machine(phydev);
++
++      return IRQ_HANDLED;
++}
++
++static int aqr_read_status(struct phy_device *phydev)
++{
++      int val;
++
++      if (phydev->autoneg == AUTONEG_ENABLE) {
++              val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1);
++              if (val < 0)
++                      return val;
++
++              linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
++                               phydev->lp_advertising,
++                               val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL);
++              linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
++                               phydev->lp_advertising,
++                               val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF);
++      }
++
++      return genphy_c45_read_status(phydev);
++}
++
++static int aqr107_read_rate(struct phy_device *phydev)
++{
++      u32 config_reg;
++      int val;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1);
++      if (val < 0)
++              return val;
++
++      if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX)
++              phydev->duplex = DUPLEX_FULL;
++      else
++              phydev->duplex = DUPLEX_HALF;
++
++      switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) {
++      case MDIO_AN_TX_VEND_STATUS1_10BASET:
++              phydev->speed = SPEED_10;
++              config_reg = VEND1_GLOBAL_CFG_10M;
++              break;
++      case MDIO_AN_TX_VEND_STATUS1_100BASETX:
++              phydev->speed = SPEED_100;
++              config_reg = VEND1_GLOBAL_CFG_100M;
++              break;
++      case MDIO_AN_TX_VEND_STATUS1_1000BASET:
++              phydev->speed = SPEED_1000;
++              config_reg = VEND1_GLOBAL_CFG_1G;
++              break;
++      case MDIO_AN_TX_VEND_STATUS1_2500BASET:
++              phydev->speed = SPEED_2500;
++              config_reg = VEND1_GLOBAL_CFG_2_5G;
++              break;
++      case MDIO_AN_TX_VEND_STATUS1_5000BASET:
++              phydev->speed = SPEED_5000;
++              config_reg = VEND1_GLOBAL_CFG_5G;
++              break;
++      case MDIO_AN_TX_VEND_STATUS1_10GBASET:
++              phydev->speed = SPEED_10000;
++              config_reg = VEND1_GLOBAL_CFG_10G;
++              break;
++      default:
++              phydev->speed = SPEED_UNKNOWN;
++              return 0;
++      }
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg);
++      if (val < 0)
++              return val;
++
++      if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) ==
++          VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE)
++              phydev->rate_matching = RATE_MATCH_PAUSE;
++      else
++              phydev->rate_matching = RATE_MATCH_NONE;
++
++      return 0;
++}
++
++static int aqr107_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;
++
++      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_KX:
++              phydev->interface = PHY_INTERFACE_MODE_1000BASEKX;
++              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_XAUI:
++              phydev->interface = PHY_INTERFACE_MODE_XAUI;
++              break;
++      case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
++              phydev->interface = PHY_INTERFACE_MODE_SGMII;
++              break;
++      case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI:
++              phydev->interface = PHY_INTERFACE_MODE_RXAUI;
++              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 possibly downshifted rate from vendor register */
++      return aqr107_read_rate(phydev);
++}
++
++static int aqr107_get_downshift(struct phy_device *phydev, u8 *data)
++{
++      int val, cnt, enable;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV);
++      if (val < 0)
++              return val;
++
++      enable = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_EN, val);
++      cnt = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val);
++
++      *data = enable && cnt ? cnt : DOWNSHIFT_DEV_DISABLE;
++
++      return 0;
++}
++
++static int aqr107_set_downshift(struct phy_device *phydev, u8 cnt)
++{
++      int val = 0;
++
++      if (!FIELD_FIT(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt))
++              return -E2BIG;
++
++      if (cnt != DOWNSHIFT_DEV_DISABLE) {
++              val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN;
++              val |= FIELD_PREP(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt);
++      }
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV,
++                            MDIO_AN_VEND_PROV_DOWNSHIFT_EN |
++                            MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val);
++}
++
++static int aqr107_get_tunable(struct phy_device *phydev,
++                            struct ethtool_tunable *tuna, void *data)
++{
++      switch (tuna->id) {
++      case ETHTOOL_PHY_DOWNSHIFT:
++              return aqr107_get_downshift(phydev, data);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++static int aqr107_set_tunable(struct phy_device *phydev,
++                            struct ethtool_tunable *tuna, const void *data)
++{
++      switch (tuna->id) {
++      case ETHTOOL_PHY_DOWNSHIFT:
++              return aqr107_set_downshift(phydev, *(const u8 *)data);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++/* If we configure settings whilst firmware is still initializing the chip,
++ * then these settings may be overwritten. Therefore make sure chip
++ * initialization has completed. Use presence of the firmware ID as
++ * indicator for initialization having completed.
++ * The chip also provides a "reset completed" bit, but it's cleared after
++ * read. Therefore function would time out if called again.
++ */
++static int aqr107_wait_reset_complete(struct phy_device *phydev)
++{
++      int val;
++
++      return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
++                                       VEND1_GLOBAL_FW_ID, val, val != 0,
++                                       20000, 2000000, false);
++}
++
++static void aqr107_chip_info(struct phy_device *phydev)
++{
++      u8 fw_major, fw_minor, build_id, prov_id;
++      int val;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
++      if (val < 0)
++              return;
++
++      fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val);
++      fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val);
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1);
++      if (val < 0)
++              return;
++
++      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",
++                 fw_major, fw_minor, build_id, prov_id);
++}
++
++static int aqr107_config_init(struct phy_device *phydev)
++{
++      int ret;
++
++      /* Check that the PHY interface type is compatible */
++      if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
++          phydev->interface != PHY_INTERFACE_MODE_1000BASEKX &&
++          phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
++          phydev->interface != PHY_INTERFACE_MODE_XGMII &&
++          phydev->interface != PHY_INTERFACE_MODE_USXGMII &&
++          phydev->interface != PHY_INTERFACE_MODE_10GKR &&
++          phydev->interface != PHY_INTERFACE_MODE_10GBASER &&
++          phydev->interface != PHY_INTERFACE_MODE_XAUI &&
++          phydev->interface != PHY_INTERFACE_MODE_RXAUI)
++              return -ENODEV;
++
++      WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII,
++           "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n");
++
++      ret = aqr107_wait_reset_complete(phydev);
++      if (!ret)
++              aqr107_chip_info(phydev);
++
++      return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
++}
++
++static int aqcs109_config_init(struct phy_device *phydev)
++{
++      int ret;
++
++      /* Check that the PHY interface type is compatible */
++      if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
++          phydev->interface != PHY_INTERFACE_MODE_2500BASEX)
++              return -ENODEV;
++
++      ret = aqr107_wait_reset_complete(phydev);
++      if (!ret)
++              aqr107_chip_info(phydev);
++
++      /* AQCS109 belongs to a chip family partially supporting 10G and 5G.
++       * PMA speed ability bits are the same for all members of the family,
++       * AQCS109 however supports speeds up to 2.5G only.
++       */
++      phy_set_max_speed(phydev, SPEED_2500);
++
++      return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
++}
++
++static void aqr107_link_change_notify(struct phy_device *phydev)
++{
++      u8 fw_major, fw_minor;
++      bool downshift, short_reach, afr;
++      int mode, val;
++
++      if (phydev->state != PHY_RUNNING || phydev->autoneg == AUTONEG_DISABLE)
++              return;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1);
++      /* call failed or link partner is no Aquantia PHY */
++      if (val < 0 || !(val & MDIO_AN_RX_LP_STAT1_AQ_PHY))
++              return;
++
++      short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH;
++      downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT4);
++      if (val < 0)
++              return;
++
++      fw_major = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MAJOR, val);
++      fw_minor = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MINOR, val);
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_VEND_STAT3);
++      if (val < 0)
++              return;
++
++      afr = val & MDIO_AN_RX_VEND_STAT3_AFR;
++
++      phydev_dbg(phydev, "Link partner is Aquantia PHY, FW %u.%u%s%s%s\n",
++                 fw_major, fw_minor,
++                 short_reach ? ", short reach mode" : "",
++                 downshift ? ", fast-retrain downshift advertised" : "",
++                 afr ? ", fast reframe advertised" : "");
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT9);
++      if (val < 0)
++              return;
++
++      mode = FIELD_GET(VEND1_GLOBAL_RSVD_STAT9_MODE, val);
++      if (mode == VEND1_GLOBAL_RSVD_STAT9_1000BT2)
++              phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n");
++}
++
++static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
++{
++      int val, err;
++
++      /* The datasheet notes to wait at least 1ms after issuing a
++       * processor intensive operation before checking.
++       * We cannot use the 'sleep_before_read' parameter of read_poll_timeout
++       * because that just determines the maximum time slept, not the minimum.
++       */
++      usleep_range(1000, 5000);
++
++      err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
++                                      VEND1_GLOBAL_GEN_STAT2, val,
++                                      !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG),
++                                      AQR107_OP_IN_PROG_SLEEP,
++                                      AQR107_OP_IN_PROG_TIMEOUT, false);
++      if (err) {
++              phydev_err(phydev, "timeout: processor-intensive MDIO operation\n");
++              return err;
++      }
++
++      return 0;
++}
++
++static int aqr107_get_rate_matching(struct phy_device *phydev,
++                                  phy_interface_t iface)
++{
++      if (iface == PHY_INTERFACE_MODE_10GBASER ||
++          iface == PHY_INTERFACE_MODE_2500BASEX ||
++          iface == PHY_INTERFACE_MODE_NA)
++              return RATE_MATCH_PAUSE;
++      return RATE_MATCH_NONE;
++}
++
++static int aqr107_suspend(struct phy_device *phydev)
++{
++      int err;
++
++      err = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
++                             MDIO_CTRL1_LPOWER);
++      if (err)
++              return err;
++
++      return aqr107_wait_processor_intensive_op(phydev);
++}
++
++static int aqr107_resume(struct phy_device *phydev)
++{
++      int err;
++
++      err = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
++                               MDIO_CTRL1_LPOWER);
++      if (err)
++              return err;
++
++      return aqr107_wait_processor_intensive_op(phydev);
++}
++
++static int aqr107_probe(struct phy_device *phydev)
++{
++      phydev->priv = devm_kzalloc(&phydev->mdio.dev,
++                                  sizeof(struct aqr107_priv), GFP_KERNEL);
++      if (!phydev->priv)
++              return -ENOMEM;
++
++      return aqr_hwmon_probe(phydev);
++}
++
++static struct phy_driver aqr_driver[] = {
++{
++      PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
++      .name           = "Aquantia AQ1202",
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr_read_status,
++},
++{
++      PHY_ID_MATCH_MODEL(PHY_ID_AQ2104),
++      .name           = "Aquantia AQ2104",
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr_read_status,
++},
++{
++      PHY_ID_MATCH_MODEL(PHY_ID_AQR105),
++      .name           = "Aquantia AQR105",
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr_read_status,
++      .suspend        = aqr107_suspend,
++      .resume         = aqr107_resume,
++},
++{
++      PHY_ID_MATCH_MODEL(PHY_ID_AQR106),
++      .name           = "Aquantia AQR106",
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr_read_status,
++},
++{
++      PHY_ID_MATCH_MODEL(PHY_ID_AQR107),
++      .name           = "Aquantia AQR107",
++      .probe          = aqr107_probe,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .config_init    = aqr107_config_init,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr107_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,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .config_init    = aqcs109_config_init,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr107_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_AQR405),
++      .name           = "Aquantia AQR405",
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr_read_status,
++},
++{
++      PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
++      .name           = "Aquantia AQR112",
++      .probe          = aqr107_probe,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .get_tunable    = aqr107_get_tunable,
++      .set_tunable    = aqr107_set_tunable,
++      .suspend        = aqr107_suspend,
++      .resume         = aqr107_resume,
++      .read_status    = aqr107_read_status,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .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_AQR412),
++      .name           = "Aquantia AQR412",
++      .probe          = aqr107_probe,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .get_tunable    = aqr107_get_tunable,
++      .set_tunable    = aqr107_set_tunable,
++      .suspend        = aqr107_suspend,
++      .resume         = aqr107_resume,
++      .read_status    = aqr107_read_status,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .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_AQR113C),
++      .name           = "Aquantia AQR113C",
++      .probe          = aqr107_probe,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .config_init    = aqr107_config_init,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt       = aqr_handle_interrupt,
++      .read_status    = aqr107_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);
++
++static struct mdio_device_id __maybe_unused aqr_tbl[] = {
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) },
++      { 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_AQCS109) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
++      { }
++};
++
++MODULE_DEVICE_TABLE(mdio, aqr_tbl);
++
++MODULE_DESCRIPTION("Aquantia PHY driver");
++MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
++MODULE_LICENSE("GPL v2");
+--- a/drivers/net/phy/aquantia_hwmon.c
++++ /dev/null
+@@ -1,250 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/* HWMON driver for Aquantia PHY
+- *
+- * Author: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
+- * Author: Andrew Lunn <andrew@lunn.ch>
+- * Author: Heiner Kallweit <hkallweit1@gmail.com>
+- */
+-
+-#include <linux/phy.h>
+-#include <linux/device.h>
+-#include <linux/ctype.h>
+-#include <linux/hwmon.h>
+-
+-#include "aquantia.h"
+-
+-/* Vendor specific 1, MDIO_MMD_VEND2 */
+-#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL     0xc421
+-#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL      0xc422
+-#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN     0xc423
+-#define VEND1_THERMAL_PROV_LOW_TEMP_WARN      0xc424
+-#define VEND1_THERMAL_STAT1                   0xc820
+-#define VEND1_THERMAL_STAT2                   0xc821
+-#define VEND1_THERMAL_STAT2_VALID             BIT(0)
+-#define VEND1_GENERAL_STAT1                   0xc830
+-#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL    BIT(14)
+-#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL     BIT(13)
+-#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN    BIT(12)
+-#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN     BIT(11)
+-
+-#if IS_REACHABLE(CONFIG_HWMON)
+-
+-static umode_t aqr_hwmon_is_visible(const void *data,
+-                                  enum hwmon_sensor_types type,
+-                                  u32 attr, int channel)
+-{
+-      if (type != hwmon_temp)
+-              return 0;
+-
+-      switch (attr) {
+-      case hwmon_temp_input:
+-      case hwmon_temp_min_alarm:
+-      case hwmon_temp_max_alarm:
+-      case hwmon_temp_lcrit_alarm:
+-      case hwmon_temp_crit_alarm:
+-              return 0444;
+-      case hwmon_temp_min:
+-      case hwmon_temp_max:
+-      case hwmon_temp_lcrit:
+-      case hwmon_temp_crit:
+-              return 0644;
+-      default:
+-              return 0;
+-      }
+-}
+-
+-static int aqr_hwmon_get(struct phy_device *phydev, int reg, long *value)
+-{
+-      int temp = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg);
+-
+-      if (temp < 0)
+-              return temp;
+-
+-      /* 16 bit value is 2's complement with LSB = 1/256th degree Celsius */
+-      *value = (s16)temp * 1000 / 256;
+-
+-      return 0;
+-}
+-
+-static int aqr_hwmon_set(struct phy_device *phydev, int reg, long value)
+-{
+-      int temp;
+-
+-      if (value >= 128000 || value < -128000)
+-              return -ERANGE;
+-
+-      temp = value * 256 / 1000;
+-
+-      /* temp is in s16 range and we're interested in lower 16 bits only */
+-      return phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, (u16)temp);
+-}
+-
+-static int aqr_hwmon_test_bit(struct phy_device *phydev, int reg, int bit)
+-{
+-      int val = phy_read_mmd(phydev, MDIO_MMD_VEND1, reg);
+-
+-      if (val < 0)
+-              return val;
+-
+-      return !!(val & bit);
+-}
+-
+-static int aqr_hwmon_status1(struct phy_device *phydev, int bit, long *value)
+-{
+-      int val = aqr_hwmon_test_bit(phydev, VEND1_GENERAL_STAT1, bit);
+-
+-      if (val < 0)
+-              return val;
+-
+-      *value = val;
+-
+-      return 0;
+-}
+-
+-static int aqr_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+-                        u32 attr, int channel, long *value)
+-{
+-      struct phy_device *phydev = dev_get_drvdata(dev);
+-      int reg;
+-
+-      if (type != hwmon_temp)
+-              return -EOPNOTSUPP;
+-
+-      switch (attr) {
+-      case hwmon_temp_input:
+-              reg = aqr_hwmon_test_bit(phydev, VEND1_THERMAL_STAT2,
+-                                       VEND1_THERMAL_STAT2_VALID);
+-              if (reg < 0)
+-                      return reg;
+-              if (!reg)
+-                      return -EBUSY;
+-
+-              return aqr_hwmon_get(phydev, VEND1_THERMAL_STAT1, value);
+-
+-      case hwmon_temp_lcrit:
+-              return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL,
+-                                   value);
+-      case hwmon_temp_min:
+-              return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN,
+-                                   value);
+-      case hwmon_temp_max:
+-              return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN,
+-                                   value);
+-      case hwmon_temp_crit:
+-              return aqr_hwmon_get(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL,
+-                                   value);
+-      case hwmon_temp_lcrit_alarm:
+-              return aqr_hwmon_status1(phydev,
+-                                       VEND1_GENERAL_STAT1_LOW_TEMP_FAIL,
+-                                       value);
+-      case hwmon_temp_min_alarm:
+-              return aqr_hwmon_status1(phydev,
+-                                       VEND1_GENERAL_STAT1_LOW_TEMP_WARN,
+-                                       value);
+-      case hwmon_temp_max_alarm:
+-              return aqr_hwmon_status1(phydev,
+-                                       VEND1_GENERAL_STAT1_HIGH_TEMP_WARN,
+-                                       value);
+-      case hwmon_temp_crit_alarm:
+-              return aqr_hwmon_status1(phydev,
+-                                       VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL,
+-                                       value);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+-static int aqr_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+-                         u32 attr, int channel, long value)
+-{
+-      struct phy_device *phydev = dev_get_drvdata(dev);
+-
+-      if (type != hwmon_temp)
+-              return -EOPNOTSUPP;
+-
+-      switch (attr) {
+-      case hwmon_temp_lcrit:
+-              return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_FAIL,
+-                                   value);
+-      case hwmon_temp_min:
+-              return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_LOW_TEMP_WARN,
+-                                   value);
+-      case hwmon_temp_max:
+-              return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_WARN,
+-                                   value);
+-      case hwmon_temp_crit:
+-              return aqr_hwmon_set(phydev, VEND1_THERMAL_PROV_HIGH_TEMP_FAIL,
+-                                   value);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+-static const struct hwmon_ops aqr_hwmon_ops = {
+-      .is_visible = aqr_hwmon_is_visible,
+-      .read = aqr_hwmon_read,
+-      .write = aqr_hwmon_write,
+-};
+-
+-static u32 aqr_hwmon_chip_config[] = {
+-      HWMON_C_REGISTER_TZ,
+-      0,
+-};
+-
+-static const struct hwmon_channel_info aqr_hwmon_chip = {
+-      .type = hwmon_chip,
+-      .config = aqr_hwmon_chip_config,
+-};
+-
+-static u32 aqr_hwmon_temp_config[] = {
+-      HWMON_T_INPUT |
+-      HWMON_T_MAX | HWMON_T_MIN |
+-      HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM |
+-      HWMON_T_CRIT | HWMON_T_LCRIT |
+-      HWMON_T_CRIT_ALARM | HWMON_T_LCRIT_ALARM,
+-      0,
+-};
+-
+-static const struct hwmon_channel_info aqr_hwmon_temp = {
+-      .type = hwmon_temp,
+-      .config = aqr_hwmon_temp_config,
+-};
+-
+-static const struct hwmon_channel_info * const aqr_hwmon_info[] = {
+-      &aqr_hwmon_chip,
+-      &aqr_hwmon_temp,
+-      NULL,
+-};
+-
+-static const struct hwmon_chip_info aqr_hwmon_chip_info = {
+-      .ops = &aqr_hwmon_ops,
+-      .info = aqr_hwmon_info,
+-};
+-
+-int aqr_hwmon_probe(struct phy_device *phydev)
+-{
+-      struct device *dev = &phydev->mdio.dev;
+-      struct device *hwmon_dev;
+-      char *hwmon_name;
+-      int i, j;
+-
+-      hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
+-      if (!hwmon_name)
+-              return -ENOMEM;
+-
+-      for (i = j = 0; hwmon_name[i]; i++) {
+-              if (isalnum(hwmon_name[i])) {
+-                      if (i != j)
+-                              hwmon_name[j] = hwmon_name[i];
+-                      j++;
+-              }
+-      }
+-      hwmon_name[j] = '\0';
+-
+-      hwmon_dev = devm_hwmon_device_register_with_info(dev, hwmon_name,
+-                                      phydev, &aqr_hwmon_chip_info, NULL);
+-
+-      return PTR_ERR_OR_ZERO(hwmon_dev);
+-}
+-
+-#endif
+--- a/drivers/net/phy/aquantia_main.c
++++ /dev/null
+@@ -1,882 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-/*
+- * Driver for Aquantia PHY
+- *
+- * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
+- *
+- * Copyright 2015 Freescale Semiconductor, Inc.
+- */
+-
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+-#include <linux/delay.h>
+-#include <linux/bitfield.h>
+-#include <linux/phy.h>
+-
+-#include "aquantia.h"
+-
+-#define PHY_ID_AQ1202 0x03a1b445
+-#define PHY_ID_AQ2104 0x03a1b460
+-#define PHY_ID_AQR105 0x03a1b4a2
+-#define PHY_ID_AQR106 0x03a1b4d0
+-#define PHY_ID_AQR107 0x03a1b4e0
+-#define PHY_ID_AQCS109        0x03a1b5c2
+-#define PHY_ID_AQR405 0x03a1b4b0
+-#define PHY_ID_AQR112 0x03a1b662
+-#define PHY_ID_AQR412 0x03a1b712
+-#define PHY_ID_AQR113C        0x31c31c12
+-
+-#define MDIO_PHYXS_VEND_IF_STATUS             0xe812
+-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK   GENMASK(7, 3)
+-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KR     0
+-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_KX     1
+-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XFI    2
+-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_USXGMII        3
+-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_XAUI   4
+-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII  6
+-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI  7
+-#define MDIO_PHYXS_VEND_IF_STATUS_TYPE_OCSGMII        10
+-
+-#define MDIO_AN_VEND_PROV                     0xc400
+-#define MDIO_AN_VEND_PROV_1000BASET_FULL      BIT(15)
+-#define MDIO_AN_VEND_PROV_1000BASET_HALF      BIT(14)
+-#define MDIO_AN_VEND_PROV_5000BASET_FULL      BIT(11)
+-#define MDIO_AN_VEND_PROV_2500BASET_FULL      BIT(10)
+-#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN                BIT(4)
+-#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK      GENMASK(3, 0)
+-#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT      4
+-
+-#define MDIO_AN_TX_VEND_STATUS1                       0xc800
+-#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK     GENMASK(3, 1)
+-#define MDIO_AN_TX_VEND_STATUS1_10BASET               0
+-#define MDIO_AN_TX_VEND_STATUS1_100BASETX     1
+-#define MDIO_AN_TX_VEND_STATUS1_1000BASET     2
+-#define MDIO_AN_TX_VEND_STATUS1_10GBASET      3
+-#define MDIO_AN_TX_VEND_STATUS1_2500BASET     4
+-#define MDIO_AN_TX_VEND_STATUS1_5000BASET     5
+-#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX   BIT(0)
+-
+-#define MDIO_AN_TX_VEND_INT_STATUS1           0xcc00
+-#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1)
+-
+-#define MDIO_AN_TX_VEND_INT_STATUS2           0xcc01
+-#define MDIO_AN_TX_VEND_INT_STATUS2_MASK      BIT(0)
+-
+-#define MDIO_AN_TX_VEND_INT_MASK2             0xd401
+-#define MDIO_AN_TX_VEND_INT_MASK2_LINK                BIT(0)
+-
+-#define MDIO_AN_RX_LP_STAT1                   0xe820
+-#define MDIO_AN_RX_LP_STAT1_1000BASET_FULL    BIT(15)
+-#define MDIO_AN_RX_LP_STAT1_1000BASET_HALF    BIT(14)
+-#define MDIO_AN_RX_LP_STAT1_SHORT_REACH               BIT(13)
+-#define MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT  BIT(12)
+-#define MDIO_AN_RX_LP_STAT1_AQ_PHY            BIT(2)
+-
+-#define MDIO_AN_RX_LP_STAT4                   0xe823
+-#define MDIO_AN_RX_LP_STAT4_FW_MAJOR          GENMASK(15, 8)
+-#define MDIO_AN_RX_LP_STAT4_FW_MINOR          GENMASK(7, 0)
+-
+-#define MDIO_AN_RX_VEND_STAT3                 0xe832
+-#define MDIO_AN_RX_VEND_STAT3_AFR             BIT(0)
+-
+-/* MDIO_MMD_C22EXT */
+-#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES         0xd292
+-#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES          0xd294
+-#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER               0xd297
+-#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES         0xd313
+-#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES          0xd315
+-#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER               0xd317
+-#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS          0xd318
+-#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS     0xd319
+-#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR     0xd31a
+-#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES         0xd31b
+-
+-/* Vendor specific 1, MDIO_MMD_VEND1 */
+-#define VEND1_GLOBAL_FW_ID                    0x0020
+-#define VEND1_GLOBAL_FW_ID_MAJOR              GENMASK(15, 8)
+-#define VEND1_GLOBAL_FW_ID_MINOR              GENMASK(7, 0)
+-
+-#define VEND1_GLOBAL_GEN_STAT2                        0xc831
+-#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG     BIT(15)
+-
+-/* The following registers all have similar layouts; first the registers... */
+-#define VEND1_GLOBAL_CFG_10M                  0x0310
+-#define VEND1_GLOBAL_CFG_100M                 0x031b
+-#define VEND1_GLOBAL_CFG_1G                   0x031c
+-#define VEND1_GLOBAL_CFG_2_5G                 0x031d
+-#define VEND1_GLOBAL_CFG_5G                   0x031e
+-#define VEND1_GLOBAL_CFG_10G                  0x031f
+-/* ...and now the fields */
+-#define VEND1_GLOBAL_CFG_RATE_ADAPT           GENMASK(8, 7)
+-#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE      0
+-#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX               1
+-#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE     2
+-
+-#define VEND1_GLOBAL_RSVD_STAT1                       0xc885
+-#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID   GENMASK(7, 4)
+-#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID               GENMASK(3, 0)
+-
+-#define VEND1_GLOBAL_RSVD_STAT9                       0xc88d
+-#define VEND1_GLOBAL_RSVD_STAT9_MODE          GENMASK(7, 0)
+-#define VEND1_GLOBAL_RSVD_STAT9_1000BT2               0x23
+-
+-#define VEND1_GLOBAL_INT_STD_STATUS           0xfc00
+-#define VEND1_GLOBAL_INT_VEND_STATUS          0xfc01
+-
+-#define VEND1_GLOBAL_INT_STD_MASK             0xff00
+-#define VEND1_GLOBAL_INT_STD_MASK_PMA1                BIT(15)
+-#define VEND1_GLOBAL_INT_STD_MASK_PMA2                BIT(14)
+-#define VEND1_GLOBAL_INT_STD_MASK_PCS1                BIT(13)
+-#define VEND1_GLOBAL_INT_STD_MASK_PCS2                BIT(12)
+-#define VEND1_GLOBAL_INT_STD_MASK_PCS3                BIT(11)
+-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1     BIT(10)
+-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2     BIT(9)
+-#define VEND1_GLOBAL_INT_STD_MASK_AN1         BIT(8)
+-#define VEND1_GLOBAL_INT_STD_MASK_AN2         BIT(7)
+-#define VEND1_GLOBAL_INT_STD_MASK_GBE         BIT(6)
+-#define VEND1_GLOBAL_INT_STD_MASK_ALL         BIT(0)
+-
+-#define VEND1_GLOBAL_INT_VEND_MASK            0xff01
+-#define VEND1_GLOBAL_INT_VEND_MASK_PMA                BIT(15)
+-#define VEND1_GLOBAL_INT_VEND_MASK_PCS                BIT(14)
+-#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS     BIT(13)
+-#define VEND1_GLOBAL_INT_VEND_MASK_AN         BIT(12)
+-#define VEND1_GLOBAL_INT_VEND_MASK_GBE                BIT(11)
+-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1    BIT(2)
+-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2    BIT(1)
+-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3    BIT(0)
+-
+-/* Sleep and timeout for checking if the Processor-Intensive
+- * MDIO operation is finished
+- */
+-#define AQR107_OP_IN_PROG_SLEEP               1000
+-#define AQR107_OP_IN_PROG_TIMEOUT     100000
+-
+-struct aqr107_hw_stat {
+-      const char *name;
+-      int reg;
+-      int size;
+-};
+-
+-#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s }
+-static const struct aqr107_hw_stat aqr107_hw_stats[] = {
+-      SGMII_STAT("sgmii_rx_good_frames",          RX_GOOD_FRAMES,     26),
+-      SGMII_STAT("sgmii_rx_bad_frames",           RX_BAD_FRAMES,      26),
+-      SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER,    8),
+-      SGMII_STAT("sgmii_tx_good_frames",          TX_GOOD_FRAMES,     26),
+-      SGMII_STAT("sgmii_tx_bad_frames",           TX_BAD_FRAMES,      26),
+-      SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER,    8),
+-      SGMII_STAT("sgmii_tx_collisions",           TX_COLLISIONS,       8),
+-      SGMII_STAT("sgmii_tx_line_collisions",      TX_LINE_COLLISIONS,  8),
+-      SGMII_STAT("sgmii_tx_frame_alignment_err",  TX_FRAME_ALIGN_ERR, 16),
+-      SGMII_STAT("sgmii_tx_runt_frames",          TX_RUNT_FRAMES,     22),
+-};
+-#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
+-
+-struct aqr107_priv {
+-      u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
+-};
+-
+-static int aqr107_get_sset_count(struct phy_device *phydev)
+-{
+-      return AQR107_SGMII_STAT_SZ;
+-}
+-
+-static void aqr107_get_strings(struct phy_device *phydev, u8 *data)
+-{
+-      int i;
+-
+-      for (i = 0; i < AQR107_SGMII_STAT_SZ; i++)
+-              strscpy(data + i * ETH_GSTRING_LEN, aqr107_hw_stats[i].name,
+-                      ETH_GSTRING_LEN);
+-}
+-
+-static u64 aqr107_get_stat(struct phy_device *phydev, int index)
+-{
+-      const struct aqr107_hw_stat *stat = aqr107_hw_stats + index;
+-      int len_l = min(stat->size, 16);
+-      int len_h = stat->size - len_l;
+-      u64 ret;
+-      int val;
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg);
+-      if (val < 0)
+-              return U64_MAX;
+-
+-      ret = val & GENMASK(len_l - 1, 0);
+-      if (len_h) {
+-              val = phy_read_mmd(phydev, MDIO_MMD_C22EXT, stat->reg + 1);
+-              if (val < 0)
+-                      return U64_MAX;
+-
+-              ret += (val & GENMASK(len_h - 1, 0)) << 16;
+-      }
+-
+-      return ret;
+-}
+-
+-static void aqr107_get_stats(struct phy_device *phydev,
+-                           struct ethtool_stats *stats, u64 *data)
+-{
+-      struct aqr107_priv *priv = phydev->priv;
+-      u64 val;
+-      int i;
+-
+-      for (i = 0; i < AQR107_SGMII_STAT_SZ; i++) {
+-              val = aqr107_get_stat(phydev, i);
+-              if (val == U64_MAX)
+-                      phydev_err(phydev, "Reading HW Statistics failed for %s\n",
+-                                 aqr107_hw_stats[i].name);
+-              else
+-                      priv->sgmii_stats[i] += val;
+-
+-              data[i] = priv->sgmii_stats[i];
+-      }
+-}
+-
+-static int aqr_config_aneg(struct phy_device *phydev)
+-{
+-      bool changed = false;
+-      u16 reg;
+-      int ret;
+-
+-      if (phydev->autoneg == AUTONEG_DISABLE)
+-              return genphy_c45_pma_setup_forced(phydev);
+-
+-      ret = genphy_c45_an_config_aneg(phydev);
+-      if (ret < 0)
+-              return ret;
+-      if (ret > 0)
+-              changed = true;
+-
+-      /* Clause 45 has no standardized support for 1000BaseT, therefore
+-       * use vendor registers for this mode.
+-       */
+-      reg = 0;
+-      if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+-                            phydev->advertising))
+-              reg |= MDIO_AN_VEND_PROV_1000BASET_FULL;
+-
+-      if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+-                            phydev->advertising))
+-              reg |= MDIO_AN_VEND_PROV_1000BASET_HALF;
+-
+-      /* Handle the case when the 2.5G and 5G speeds are not advertised */
+-      if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+-                            phydev->advertising))
+-              reg |= MDIO_AN_VEND_PROV_2500BASET_FULL;
+-
+-      if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+-                            phydev->advertising))
+-              reg |= MDIO_AN_VEND_PROV_5000BASET_FULL;
+-
+-      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV,
+-                                   MDIO_AN_VEND_PROV_1000BASET_HALF |
+-                                   MDIO_AN_VEND_PROV_1000BASET_FULL |
+-                                   MDIO_AN_VEND_PROV_2500BASET_FULL |
+-                                   MDIO_AN_VEND_PROV_5000BASET_FULL, reg);
+-      if (ret < 0)
+-              return ret;
+-      if (ret > 0)
+-              changed = true;
+-
+-      return genphy_c45_check_and_restart_aneg(phydev, changed);
+-}
+-
+-static int aqr_config_intr(struct phy_device *phydev)
+-{
+-      bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED;
+-      int err;
+-
+-      if (en) {
+-              /* Clear any pending interrupts before enabling them */
+-              err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2);
+-              if (err < 0)
+-                      return err;
+-      }
+-
+-      err = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_MASK2,
+-                          en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0);
+-      if (err < 0)
+-              return err;
+-
+-      err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK,
+-                          en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0);
+-      if (err < 0)
+-              return err;
+-
+-      err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK,
+-                          en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 |
+-                          VEND1_GLOBAL_INT_VEND_MASK_AN : 0);
+-      if (err < 0)
+-              return err;
+-
+-      if (!en) {
+-              /* Clear any pending interrupts after we have disabled them */
+-              err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2);
+-              if (err < 0)
+-                      return err;
+-      }
+-
+-      return 0;
+-}
+-
+-static irqreturn_t aqr_handle_interrupt(struct phy_device *phydev)
+-{
+-      int irq_status;
+-
+-      irq_status = phy_read_mmd(phydev, MDIO_MMD_AN,
+-                                MDIO_AN_TX_VEND_INT_STATUS2);
+-      if (irq_status < 0) {
+-              phy_error(phydev);
+-              return IRQ_NONE;
+-      }
+-
+-      if (!(irq_status & MDIO_AN_TX_VEND_INT_STATUS2_MASK))
+-              return IRQ_NONE;
+-
+-      phy_trigger_machine(phydev);
+-
+-      return IRQ_HANDLED;
+-}
+-
+-static int aqr_read_status(struct phy_device *phydev)
+-{
+-      int val;
+-
+-      if (phydev->autoneg == AUTONEG_ENABLE) {
+-              val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1);
+-              if (val < 0)
+-                      return val;
+-
+-              linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+-                               phydev->lp_advertising,
+-                               val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL);
+-              linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+-                               phydev->lp_advertising,
+-                               val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF);
+-      }
+-
+-      return genphy_c45_read_status(phydev);
+-}
+-
+-static int aqr107_read_rate(struct phy_device *phydev)
+-{
+-      u32 config_reg;
+-      int val;
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1);
+-      if (val < 0)
+-              return val;
+-
+-      if (val & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX)
+-              phydev->duplex = DUPLEX_FULL;
+-      else
+-              phydev->duplex = DUPLEX_HALF;
+-
+-      switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, val)) {
+-      case MDIO_AN_TX_VEND_STATUS1_10BASET:
+-              phydev->speed = SPEED_10;
+-              config_reg = VEND1_GLOBAL_CFG_10M;
+-              break;
+-      case MDIO_AN_TX_VEND_STATUS1_100BASETX:
+-              phydev->speed = SPEED_100;
+-              config_reg = VEND1_GLOBAL_CFG_100M;
+-              break;
+-      case MDIO_AN_TX_VEND_STATUS1_1000BASET:
+-              phydev->speed = SPEED_1000;
+-              config_reg = VEND1_GLOBAL_CFG_1G;
+-              break;
+-      case MDIO_AN_TX_VEND_STATUS1_2500BASET:
+-              phydev->speed = SPEED_2500;
+-              config_reg = VEND1_GLOBAL_CFG_2_5G;
+-              break;
+-      case MDIO_AN_TX_VEND_STATUS1_5000BASET:
+-              phydev->speed = SPEED_5000;
+-              config_reg = VEND1_GLOBAL_CFG_5G;
+-              break;
+-      case MDIO_AN_TX_VEND_STATUS1_10GBASET:
+-              phydev->speed = SPEED_10000;
+-              config_reg = VEND1_GLOBAL_CFG_10G;
+-              break;
+-      default:
+-              phydev->speed = SPEED_UNKNOWN;
+-              return 0;
+-      }
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, config_reg);
+-      if (val < 0)
+-              return val;
+-
+-      if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) ==
+-          VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE)
+-              phydev->rate_matching = RATE_MATCH_PAUSE;
+-      else
+-              phydev->rate_matching = RATE_MATCH_NONE;
+-
+-      return 0;
+-}
+-
+-static int aqr107_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;
+-
+-      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_KX:
+-              phydev->interface = PHY_INTERFACE_MODE_1000BASEKX;
+-              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_XAUI:
+-              phydev->interface = PHY_INTERFACE_MODE_XAUI;
+-              break;
+-      case MDIO_PHYXS_VEND_IF_STATUS_TYPE_SGMII:
+-              phydev->interface = PHY_INTERFACE_MODE_SGMII;
+-              break;
+-      case MDIO_PHYXS_VEND_IF_STATUS_TYPE_RXAUI:
+-              phydev->interface = PHY_INTERFACE_MODE_RXAUI;
+-              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 possibly downshifted rate from vendor register */
+-      return aqr107_read_rate(phydev);
+-}
+-
+-static int aqr107_get_downshift(struct phy_device *phydev, u8 *data)
+-{
+-      int val, cnt, enable;
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV);
+-      if (val < 0)
+-              return val;
+-
+-      enable = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_EN, val);
+-      cnt = FIELD_GET(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val);
+-
+-      *data = enable && cnt ? cnt : DOWNSHIFT_DEV_DISABLE;
+-
+-      return 0;
+-}
+-
+-static int aqr107_set_downshift(struct phy_device *phydev, u8 cnt)
+-{
+-      int val = 0;
+-
+-      if (!FIELD_FIT(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt))
+-              return -E2BIG;
+-
+-      if (cnt != DOWNSHIFT_DEV_DISABLE) {
+-              val = MDIO_AN_VEND_PROV_DOWNSHIFT_EN;
+-              val |= FIELD_PREP(MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, cnt);
+-      }
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV,
+-                            MDIO_AN_VEND_PROV_DOWNSHIFT_EN |
+-                            MDIO_AN_VEND_PROV_DOWNSHIFT_MASK, val);
+-}
+-
+-static int aqr107_get_tunable(struct phy_device *phydev,
+-                            struct ethtool_tunable *tuna, void *data)
+-{
+-      switch (tuna->id) {
+-      case ETHTOOL_PHY_DOWNSHIFT:
+-              return aqr107_get_downshift(phydev, data);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+-static int aqr107_set_tunable(struct phy_device *phydev,
+-                            struct ethtool_tunable *tuna, const void *data)
+-{
+-      switch (tuna->id) {
+-      case ETHTOOL_PHY_DOWNSHIFT:
+-              return aqr107_set_downshift(phydev, *(const u8 *)data);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+-/* If we configure settings whilst firmware is still initializing the chip,
+- * then these settings may be overwritten. Therefore make sure chip
+- * initialization has completed. Use presence of the firmware ID as
+- * indicator for initialization having completed.
+- * The chip also provides a "reset completed" bit, but it's cleared after
+- * read. Therefore function would time out if called again.
+- */
+-static int aqr107_wait_reset_complete(struct phy_device *phydev)
+-{
+-      int val;
+-
+-      return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
+-                                       VEND1_GLOBAL_FW_ID, val, val != 0,
+-                                       20000, 2000000, false);
+-}
+-
+-static void aqr107_chip_info(struct phy_device *phydev)
+-{
+-      u8 fw_major, fw_minor, build_id, prov_id;
+-      int val;
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
+-      if (val < 0)
+-              return;
+-
+-      fw_major = FIELD_GET(VEND1_GLOBAL_FW_ID_MAJOR, val);
+-      fw_minor = FIELD_GET(VEND1_GLOBAL_FW_ID_MINOR, val);
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT1);
+-      if (val < 0)
+-              return;
+-
+-      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",
+-                 fw_major, fw_minor, build_id, prov_id);
+-}
+-
+-static int aqr107_config_init(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      /* Check that the PHY interface type is compatible */
+-      if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
+-          phydev->interface != PHY_INTERFACE_MODE_1000BASEKX &&
+-          phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
+-          phydev->interface != PHY_INTERFACE_MODE_XGMII &&
+-          phydev->interface != PHY_INTERFACE_MODE_USXGMII &&
+-          phydev->interface != PHY_INTERFACE_MODE_10GKR &&
+-          phydev->interface != PHY_INTERFACE_MODE_10GBASER &&
+-          phydev->interface != PHY_INTERFACE_MODE_XAUI &&
+-          phydev->interface != PHY_INTERFACE_MODE_RXAUI)
+-              return -ENODEV;
+-
+-      WARN(phydev->interface == PHY_INTERFACE_MODE_XGMII,
+-           "Your devicetree is out of date, please update it. The AQR107 family doesn't support XGMII, maybe you mean USXGMII.\n");
+-
+-      ret = aqr107_wait_reset_complete(phydev);
+-      if (!ret)
+-              aqr107_chip_info(phydev);
+-
+-      return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
+-}
+-
+-static int aqcs109_config_init(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      /* Check that the PHY interface type is compatible */
+-      if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
+-          phydev->interface != PHY_INTERFACE_MODE_2500BASEX)
+-              return -ENODEV;
+-
+-      ret = aqr107_wait_reset_complete(phydev);
+-      if (!ret)
+-              aqr107_chip_info(phydev);
+-
+-      /* AQCS109 belongs to a chip family partially supporting 10G and 5G.
+-       * PMA speed ability bits are the same for all members of the family,
+-       * AQCS109 however supports speeds up to 2.5G only.
+-       */
+-      phy_set_max_speed(phydev, SPEED_2500);
+-
+-      return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
+-}
+-
+-static void aqr107_link_change_notify(struct phy_device *phydev)
+-{
+-      u8 fw_major, fw_minor;
+-      bool downshift, short_reach, afr;
+-      int mode, val;
+-
+-      if (phydev->state != PHY_RUNNING || phydev->autoneg == AUTONEG_DISABLE)
+-              return;
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1);
+-      /* call failed or link partner is no Aquantia PHY */
+-      if (val < 0 || !(val & MDIO_AN_RX_LP_STAT1_AQ_PHY))
+-              return;
+-
+-      short_reach = val & MDIO_AN_RX_LP_STAT1_SHORT_REACH;
+-      downshift = val & MDIO_AN_RX_LP_STAT1_AQRATE_DOWNSHIFT;
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT4);
+-      if (val < 0)
+-              return;
+-
+-      fw_major = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MAJOR, val);
+-      fw_minor = FIELD_GET(MDIO_AN_RX_LP_STAT4_FW_MINOR, val);
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_VEND_STAT3);
+-      if (val < 0)
+-              return;
+-
+-      afr = val & MDIO_AN_RX_VEND_STAT3_AFR;
+-
+-      phydev_dbg(phydev, "Link partner is Aquantia PHY, FW %u.%u%s%s%s\n",
+-                 fw_major, fw_minor,
+-                 short_reach ? ", short reach mode" : "",
+-                 downshift ? ", fast-retrain downshift advertised" : "",
+-                 afr ? ", fast reframe advertised" : "");
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_RSVD_STAT9);
+-      if (val < 0)
+-              return;
+-
+-      mode = FIELD_GET(VEND1_GLOBAL_RSVD_STAT9_MODE, val);
+-      if (mode == VEND1_GLOBAL_RSVD_STAT9_1000BT2)
+-              phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n");
+-}
+-
+-static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
+-{
+-      int val, err;
+-
+-      /* The datasheet notes to wait at least 1ms after issuing a
+-       * processor intensive operation before checking.
+-       * We cannot use the 'sleep_before_read' parameter of read_poll_timeout
+-       * because that just determines the maximum time slept, not the minimum.
+-       */
+-      usleep_range(1000, 5000);
+-
+-      err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
+-                                      VEND1_GLOBAL_GEN_STAT2, val,
+-                                      !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG),
+-                                      AQR107_OP_IN_PROG_SLEEP,
+-                                      AQR107_OP_IN_PROG_TIMEOUT, false);
+-      if (err) {
+-              phydev_err(phydev, "timeout: processor-intensive MDIO operation\n");
+-              return err;
+-      }
+-
+-      return 0;
+-}
+-
+-static int aqr107_get_rate_matching(struct phy_device *phydev,
+-                                  phy_interface_t iface)
+-{
+-      if (iface == PHY_INTERFACE_MODE_10GBASER ||
+-          iface == PHY_INTERFACE_MODE_2500BASEX ||
+-          iface == PHY_INTERFACE_MODE_NA)
+-              return RATE_MATCH_PAUSE;
+-      return RATE_MATCH_NONE;
+-}
+-
+-static int aqr107_suspend(struct phy_device *phydev)
+-{
+-      int err;
+-
+-      err = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
+-                             MDIO_CTRL1_LPOWER);
+-      if (err)
+-              return err;
+-
+-      return aqr107_wait_processor_intensive_op(phydev);
+-}
+-
+-static int aqr107_resume(struct phy_device *phydev)
+-{
+-      int err;
+-
+-      err = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MDIO_CTRL1,
+-                               MDIO_CTRL1_LPOWER);
+-      if (err)
+-              return err;
+-
+-      return aqr107_wait_processor_intensive_op(phydev);
+-}
+-
+-static int aqr107_probe(struct phy_device *phydev)
+-{
+-      phydev->priv = devm_kzalloc(&phydev->mdio.dev,
+-                                  sizeof(struct aqr107_priv), GFP_KERNEL);
+-      if (!phydev->priv)
+-              return -ENOMEM;
+-
+-      return aqr_hwmon_probe(phydev);
+-}
+-
+-static struct phy_driver aqr_driver[] = {
+-{
+-      PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
+-      .name           = "Aquantia AQ1202",
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt = aqr_handle_interrupt,
+-      .read_status    = aqr_read_status,
+-},
+-{
+-      PHY_ID_MATCH_MODEL(PHY_ID_AQ2104),
+-      .name           = "Aquantia AQ2104",
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt = aqr_handle_interrupt,
+-      .read_status    = aqr_read_status,
+-},
+-{
+-      PHY_ID_MATCH_MODEL(PHY_ID_AQR105),
+-      .name           = "Aquantia AQR105",
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt = aqr_handle_interrupt,
+-      .read_status    = aqr_read_status,
+-      .suspend        = aqr107_suspend,
+-      .resume         = aqr107_resume,
+-},
+-{
+-      PHY_ID_MATCH_MODEL(PHY_ID_AQR106),
+-      .name           = "Aquantia AQR106",
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt = aqr_handle_interrupt,
+-      .read_status    = aqr_read_status,
+-},
+-{
+-      PHY_ID_MATCH_MODEL(PHY_ID_AQR107),
+-      .name           = "Aquantia AQR107",
+-      .probe          = aqr107_probe,
+-      .get_rate_matching = aqr107_get_rate_matching,
+-      .config_init    = aqr107_config_init,
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt = aqr_handle_interrupt,
+-      .read_status    = aqr107_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,
+-      .get_rate_matching = aqr107_get_rate_matching,
+-      .config_init    = aqcs109_config_init,
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt = aqr_handle_interrupt,
+-      .read_status    = aqr107_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_AQR405),
+-      .name           = "Aquantia AQR405",
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt = aqr_handle_interrupt,
+-      .read_status    = aqr_read_status,
+-},
+-{
+-      PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
+-      .name           = "Aquantia AQR112",
+-      .probe          = aqr107_probe,
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt = aqr_handle_interrupt,
+-      .get_tunable    = aqr107_get_tunable,
+-      .set_tunable    = aqr107_set_tunable,
+-      .suspend        = aqr107_suspend,
+-      .resume         = aqr107_resume,
+-      .read_status    = aqr107_read_status,
+-      .get_rate_matching = aqr107_get_rate_matching,
+-      .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_AQR412),
+-      .name           = "Aquantia AQR412",
+-      .probe          = aqr107_probe,
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt = aqr_handle_interrupt,
+-      .get_tunable    = aqr107_get_tunable,
+-      .set_tunable    = aqr107_set_tunable,
+-      .suspend        = aqr107_suspend,
+-      .resume         = aqr107_resume,
+-      .read_status    = aqr107_read_status,
+-      .get_rate_matching = aqr107_get_rate_matching,
+-      .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_AQR113C),
+-      .name           = "Aquantia AQR113C",
+-      .probe          = aqr107_probe,
+-      .get_rate_matching = aqr107_get_rate_matching,
+-      .config_init    = aqr107_config_init,
+-      .config_aneg    = aqr_config_aneg,
+-      .config_intr    = aqr_config_intr,
+-      .handle_interrupt       = aqr_handle_interrupt,
+-      .read_status    = aqr107_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);
+-
+-static struct mdio_device_id __maybe_unused aqr_tbl[] = {
+-      { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) },
+-      { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) },
+-      { 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_AQCS109) },
+-      { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
+-      { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
+-      { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
+-      { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+-      { }
+-};
+-
+-MODULE_DEVICE_TABLE(mdio, aqr_tbl);
+-
+-MODULE_DESCRIPTION("Aquantia PHY driver");
+-MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
+-MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/backport-6.6/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch b/target/linux/generic/backport-6.6/702-02-v6.7-net-phy-aquantia-move-MMD_VEND-define-to-header.patch
new file mode 100644 (file)
index 0000000..66fbf24
--- /dev/null
@@ -0,0 +1,183 @@
+From e1fbfa4a995d42e02e22b0dff2f8b4fdee1504b3 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 14 Nov 2023 15:08:42 +0100
+Subject: [PATCH 2/3] net: phy: aquantia: move MMD_VEND define to header
+
+Move MMD_VEND define to header to clean things up and in preparation for
+firmware loading support that require some define placed in
+aquantia_main.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/aquantia/aquantia.h       | 69 +++++++++++++++++++++++
+ drivers/net/phy/aquantia/aquantia_hwmon.c | 14 -----
+ drivers/net/phy/aquantia/aquantia_main.c  | 55 ------------------
+ 3 files changed, 69 insertions(+), 69 deletions(-)
+
+--- a/drivers/net/phy/aquantia/aquantia.h
++++ b/drivers/net/phy/aquantia/aquantia.h
+@@ -9,6 +9,75 @@
+ #include <linux/device.h>
+ #include <linux/phy.h>
++/* Vendor specific 1, MDIO_MMD_VEND1 */
++#define VEND1_GLOBAL_FW_ID                    0x0020
++#define VEND1_GLOBAL_FW_ID_MAJOR              GENMASK(15, 8)
++#define VEND1_GLOBAL_FW_ID_MINOR              GENMASK(7, 0)
++
++/* The following registers all have similar layouts; first the registers... */
++#define VEND1_GLOBAL_CFG_10M                  0x0310
++#define VEND1_GLOBAL_CFG_100M                 0x031b
++#define VEND1_GLOBAL_CFG_1G                   0x031c
++#define VEND1_GLOBAL_CFG_2_5G                 0x031d
++#define VEND1_GLOBAL_CFG_5G                   0x031e
++#define VEND1_GLOBAL_CFG_10G                  0x031f
++/* ...and now the fields */
++#define VEND1_GLOBAL_CFG_RATE_ADAPT           GENMASK(8, 7)
++#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE      0
++#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX               1
++#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE     2
++
++/* Vendor specific 1, MDIO_MMD_VEND2 */
++#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL     0xc421
++#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL      0xc422
++#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN     0xc423
++#define VEND1_THERMAL_PROV_LOW_TEMP_WARN      0xc424
++#define VEND1_THERMAL_STAT1                   0xc820
++#define VEND1_THERMAL_STAT2                   0xc821
++#define VEND1_THERMAL_STAT2_VALID             BIT(0)
++#define VEND1_GENERAL_STAT1                   0xc830
++#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL    BIT(14)
++#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL     BIT(13)
++#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN    BIT(12)
++#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN     BIT(11)
++
++#define VEND1_GLOBAL_GEN_STAT2                        0xc831
++#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG     BIT(15)
++
++#define VEND1_GLOBAL_RSVD_STAT1                       0xc885
++#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID   GENMASK(7, 4)
++#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID               GENMASK(3, 0)
++
++#define VEND1_GLOBAL_RSVD_STAT9                       0xc88d
++#define VEND1_GLOBAL_RSVD_STAT9_MODE          GENMASK(7, 0)
++#define VEND1_GLOBAL_RSVD_STAT9_1000BT2               0x23
++
++#define VEND1_GLOBAL_INT_STD_STATUS           0xfc00
++#define VEND1_GLOBAL_INT_VEND_STATUS          0xfc01
++
++#define VEND1_GLOBAL_INT_STD_MASK             0xff00
++#define VEND1_GLOBAL_INT_STD_MASK_PMA1                BIT(15)
++#define VEND1_GLOBAL_INT_STD_MASK_PMA2                BIT(14)
++#define VEND1_GLOBAL_INT_STD_MASK_PCS1                BIT(13)
++#define VEND1_GLOBAL_INT_STD_MASK_PCS2                BIT(12)
++#define VEND1_GLOBAL_INT_STD_MASK_PCS3                BIT(11)
++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1     BIT(10)
++#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2     BIT(9)
++#define VEND1_GLOBAL_INT_STD_MASK_AN1         BIT(8)
++#define VEND1_GLOBAL_INT_STD_MASK_AN2         BIT(7)
++#define VEND1_GLOBAL_INT_STD_MASK_GBE         BIT(6)
++#define VEND1_GLOBAL_INT_STD_MASK_ALL         BIT(0)
++
++#define VEND1_GLOBAL_INT_VEND_MASK            0xff01
++#define VEND1_GLOBAL_INT_VEND_MASK_PMA                BIT(15)
++#define VEND1_GLOBAL_INT_VEND_MASK_PCS                BIT(14)
++#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS     BIT(13)
++#define VEND1_GLOBAL_INT_VEND_MASK_AN         BIT(12)
++#define VEND1_GLOBAL_INT_VEND_MASK_GBE                BIT(11)
++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1    BIT(2)
++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2    BIT(1)
++#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3    BIT(0)
++
+ #if IS_REACHABLE(CONFIG_HWMON)
+ int aqr_hwmon_probe(struct phy_device *phydev);
+ #else
+--- a/drivers/net/phy/aquantia/aquantia_hwmon.c
++++ b/drivers/net/phy/aquantia/aquantia_hwmon.c
+@@ -13,20 +13,6 @@
+ #include "aquantia.h"
+-/* Vendor specific 1, MDIO_MMD_VEND2 */
+-#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL     0xc421
+-#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL      0xc422
+-#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN     0xc423
+-#define VEND1_THERMAL_PROV_LOW_TEMP_WARN      0xc424
+-#define VEND1_THERMAL_STAT1                   0xc820
+-#define VEND1_THERMAL_STAT2                   0xc821
+-#define VEND1_THERMAL_STAT2_VALID             BIT(0)
+-#define VEND1_GENERAL_STAT1                   0xc830
+-#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL    BIT(14)
+-#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL     BIT(13)
+-#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN    BIT(12)
+-#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN     BIT(11)
+-
+ #if IS_REACHABLE(CONFIG_HWMON)
+ static umode_t aqr_hwmon_is_visible(const void *data,
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -91,61 +91,6 @@
+ #define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR     0xd31a
+ #define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES         0xd31b
+-/* Vendor specific 1, MDIO_MMD_VEND1 */
+-#define VEND1_GLOBAL_FW_ID                    0x0020
+-#define VEND1_GLOBAL_FW_ID_MAJOR              GENMASK(15, 8)
+-#define VEND1_GLOBAL_FW_ID_MINOR              GENMASK(7, 0)
+-
+-#define VEND1_GLOBAL_GEN_STAT2                        0xc831
+-#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG     BIT(15)
+-
+-/* The following registers all have similar layouts; first the registers... */
+-#define VEND1_GLOBAL_CFG_10M                  0x0310
+-#define VEND1_GLOBAL_CFG_100M                 0x031b
+-#define VEND1_GLOBAL_CFG_1G                   0x031c
+-#define VEND1_GLOBAL_CFG_2_5G                 0x031d
+-#define VEND1_GLOBAL_CFG_5G                   0x031e
+-#define VEND1_GLOBAL_CFG_10G                  0x031f
+-/* ...and now the fields */
+-#define VEND1_GLOBAL_CFG_RATE_ADAPT           GENMASK(8, 7)
+-#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE      0
+-#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX               1
+-#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE     2
+-
+-#define VEND1_GLOBAL_RSVD_STAT1                       0xc885
+-#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID   GENMASK(7, 4)
+-#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID               GENMASK(3, 0)
+-
+-#define VEND1_GLOBAL_RSVD_STAT9                       0xc88d
+-#define VEND1_GLOBAL_RSVD_STAT9_MODE          GENMASK(7, 0)
+-#define VEND1_GLOBAL_RSVD_STAT9_1000BT2               0x23
+-
+-#define VEND1_GLOBAL_INT_STD_STATUS           0xfc00
+-#define VEND1_GLOBAL_INT_VEND_STATUS          0xfc01
+-
+-#define VEND1_GLOBAL_INT_STD_MASK             0xff00
+-#define VEND1_GLOBAL_INT_STD_MASK_PMA1                BIT(15)
+-#define VEND1_GLOBAL_INT_STD_MASK_PMA2                BIT(14)
+-#define VEND1_GLOBAL_INT_STD_MASK_PCS1                BIT(13)
+-#define VEND1_GLOBAL_INT_STD_MASK_PCS2                BIT(12)
+-#define VEND1_GLOBAL_INT_STD_MASK_PCS3                BIT(11)
+-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1     BIT(10)
+-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2     BIT(9)
+-#define VEND1_GLOBAL_INT_STD_MASK_AN1         BIT(8)
+-#define VEND1_GLOBAL_INT_STD_MASK_AN2         BIT(7)
+-#define VEND1_GLOBAL_INT_STD_MASK_GBE         BIT(6)
+-#define VEND1_GLOBAL_INT_STD_MASK_ALL         BIT(0)
+-
+-#define VEND1_GLOBAL_INT_VEND_MASK            0xff01
+-#define VEND1_GLOBAL_INT_VEND_MASK_PMA                BIT(15)
+-#define VEND1_GLOBAL_INT_VEND_MASK_PCS                BIT(14)
+-#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS     BIT(13)
+-#define VEND1_GLOBAL_INT_VEND_MASK_AN         BIT(12)
+-#define VEND1_GLOBAL_INT_VEND_MASK_GBE                BIT(11)
+-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1    BIT(2)
+-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2    BIT(1)
+-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3    BIT(0)
+-
+ /* Sleep and timeout for checking if the Processor-Intensive
+  * MDIO operation is finished
+  */
diff --git a/target/linux/generic/backport-6.6/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch b/target/linux/generic/backport-6.6/702-03-v6.7-net-phy-aquantia-add-firmware-load-support.patch
new file mode 100644 (file)
index 0000000..1ae5966
--- /dev/null
@@ -0,0 +1,504 @@
+From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Tue, 14 Nov 2023 15:08:43 +0100
+Subject: [PATCH 3/3] net: phy: aquantia: add firmware load support
+
+Aquantia PHY-s require firmware to be loaded before they start operating.
+It can be automatically loaded in case when there is a SPI-NOR connected
+to Aquantia PHY-s or can be loaded from the host via MDIO.
+
+This patch adds support for loading the firmware via MDIO as in most cases
+there is no SPI-NOR being used to save on cost.
+Firmware loading code itself is ported from mainline U-boot with cleanups.
+
+The firmware has mixed values both in big and little endian.
+PHY core itself is big-endian but it expects values to be in little-endian.
+The firmware is little-endian but CRC-16 value for it is stored at the end
+of firmware in big-endian.
+
+It seems the PHY does the conversion internally from firmware that is
+little-endian to the PHY that is big-endian on using the mailbox
+but mailbox returns a big-endian CRC-16 to verify the written data
+integrity.
+
+Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/aquantia/Kconfig             |   1 +
+ drivers/net/phy/aquantia/Makefile            |   2 +-
+ drivers/net/phy/aquantia/aquantia.h          |  32 ++
+ drivers/net/phy/aquantia/aquantia_firmware.c | 370 +++++++++++++++++++
+ drivers/net/phy/aquantia/aquantia_main.c     |   6 +
+ 5 files changed, 410 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/phy/aquantia/aquantia_firmware.c
+
+--- a/drivers/net/phy/aquantia/Kconfig
++++ b/drivers/net/phy/aquantia/Kconfig
+@@ -1,5 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config AQUANTIA_PHY
+       tristate "Aquantia PHYs"
++      select CRC_CCITT
+       help
+         Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
+--- a/drivers/net/phy/aquantia/Makefile
++++ b/drivers/net/phy/aquantia/Makefile
+@@ -1,5 +1,5 @@
+ # SPDX-License-Identifier: GPL-2.0
+-aquantia-objs                 += aquantia_main.o
++aquantia-objs                 += aquantia_main.o aquantia_firmware.o
+ ifdef CONFIG_HWMON
+ aquantia-objs                 += aquantia_hwmon.o
+ endif
+--- a/drivers/net/phy/aquantia/aquantia.h
++++ b/drivers/net/phy/aquantia/aquantia.h
+@@ -10,10 +10,35 @@
+ #include <linux/phy.h>
+ /* Vendor specific 1, MDIO_MMD_VEND1 */
++#define VEND1_GLOBAL_SC                               0x0
++#define VEND1_GLOBAL_SC_SOFT_RESET            BIT(15)
++#define VEND1_GLOBAL_SC_LOW_POWER             BIT(11)
++
+ #define VEND1_GLOBAL_FW_ID                    0x0020
+ #define VEND1_GLOBAL_FW_ID_MAJOR              GENMASK(15, 8)
+ #define VEND1_GLOBAL_FW_ID_MINOR              GENMASK(7, 0)
++#define VEND1_GLOBAL_MAILBOX_INTERFACE1                       0x0200
++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE               BIT(15)
++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE         BIT(14)
++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET     BIT(12)
++#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY          BIT(8)
++
++#define VEND1_GLOBAL_MAILBOX_INTERFACE2                       0x0201
++#define VEND1_GLOBAL_MAILBOX_INTERFACE3                       0x0202
++#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK GENMASK(15, 0)
++#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x)   FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16))
++#define VEND1_GLOBAL_MAILBOX_INTERFACE4                       0x0203
++#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK GENMASK(15, 2)
++#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x)   FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x))
++
++#define VEND1_GLOBAL_MAILBOX_INTERFACE5                       0x0204
++#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK GENMASK(15, 0)
++#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x)   FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16))
++#define VEND1_GLOBAL_MAILBOX_INTERFACE6                       0x0205
++#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK GENMASK(15, 0)
++#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x)   FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x))
++
+ /* The following registers all have similar layouts; first the registers... */
+ #define VEND1_GLOBAL_CFG_10M                  0x0310
+ #define VEND1_GLOBAL_CFG_100M                 0x031b
+@@ -28,6 +53,11 @@
+ #define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE     2
+ /* Vendor specific 1, MDIO_MMD_VEND2 */
++#define VEND1_GLOBAL_CONTROL2                 0xc001
++#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST        BIT(15)
++#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD        BIT(6)
++#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL    BIT(0)
++
+ #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL     0xc421
+ #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL      0xc422
+ #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN     0xc423
+@@ -83,3 +113,5 @@ int aqr_hwmon_probe(struct phy_device *p
+ #else
+ static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; }
+ #endif
++
++int aqr_firmware_load(struct phy_device *phydev);
+--- /dev/null
++++ b/drivers/net/phy/aquantia/aquantia_firmware.c
+@@ -0,0 +1,370 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <linux/bitfield.h>
++#include <linux/of.h>
++#include <linux/firmware.h>
++#include <linux/crc-ccitt.h>
++#include <linux/nvmem-consumer.h>
++
++#include <asm/unaligned.h>
++
++#include "aquantia.h"
++
++#define UP_RESET_SLEEP                100
++
++/* addresses of memory segments in the phy */
++#define DRAM_BASE_ADDR                0x3FFE0000
++#define IRAM_BASE_ADDR                0x40000000
++
++/* firmware image format constants */
++#define VERSION_STRING_SIZE           0x40
++#define VERSION_STRING_OFFSET         0x0200
++/* primary offset is written at an offset from the start of the fw blob */
++#define PRIMARY_OFFSET_OFFSET         0x8
++/* primary offset needs to be then added to a base offset */
++#define PRIMARY_OFFSET_SHIFT          12
++#define PRIMARY_OFFSET(x)             ((x) << PRIMARY_OFFSET_SHIFT)
++#define HEADER_OFFSET                 0x300
++
++struct aqr_fw_header {
++      u32 padding;
++      u8 iram_offset[3];
++      u8 iram_size[3];
++      u8 dram_offset[3];
++      u8 dram_size[3];
++} __packed;
++
++enum aqr_fw_src {
++      AQR_FW_SRC_NVMEM = 0,
++      AQR_FW_SRC_FS,
++};
++
++static const char * const aqr_fw_src_string[] = {
++      [AQR_FW_SRC_NVMEM] = "NVMEM",
++      [AQR_FW_SRC_FS] = "FS",
++};
++
++/* AQR firmware doesn't have fixed offsets for iram and dram section
++ * but instead provide an header with the offset to use on reading
++ * and parsing the firmware.
++ *
++ * AQR firmware can't be trusted and each offset is validated to be
++ * not negative and be in the size of the firmware itself.
++ */
++static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size)
++{
++      return offset + get_size <= size;
++}
++
++static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value)
++{
++      if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
++              return -EINVAL;
++
++      *value = get_unaligned_be16(data + offset);
++
++      return 0;
++}
++
++static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value)
++{
++      if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
++              return -EINVAL;
++
++      *value = get_unaligned_le16(data + offset);
++
++      return 0;
++}
++
++static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value)
++{
++      if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3))
++              return -EINVAL;
++
++      *value = get_unaligned_le24(data + offset);
++
++      return 0;
++}
++
++/* load data into the phy's memory */
++static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr,
++                            const u8 *data, size_t len)
++{
++      u16 crc = 0, up_crc;
++      size_t pos;
++
++      /* PHY expect addr in LE */
++      addr = (__force u32)cpu_to_le32(addr);
++
++      phy_write_mmd(phydev, MDIO_MMD_VEND1,
++                    VEND1_GLOBAL_MAILBOX_INTERFACE1,
++                    VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
++      phy_write_mmd(phydev, MDIO_MMD_VEND1,
++                    VEND1_GLOBAL_MAILBOX_INTERFACE3,
++                    VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr));
++      phy_write_mmd(phydev, MDIO_MMD_VEND1,
++                    VEND1_GLOBAL_MAILBOX_INTERFACE4,
++                    VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr));
++
++      /* We assume and enforce the size to be word aligned.
++       * If a firmware that is not word aligned is found, please report upstream.
++       */
++      for (pos = 0; pos < len; pos += sizeof(u32)) {
++              u32 word;
++
++              /* FW data is always stored in little-endian */
++              word = get_unaligned((const u32 *)(data + pos));
++
++              phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
++                            VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
++              phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6,
++                            VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word));
++
++              phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1,
++                            VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |
++                            VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);
++
++              /* calculate CRC as we load data to the mailbox.
++               * We convert word to big-endian as PHY is BE and mailbox will
++               * return a BE CRC.
++               */
++              word = (__force u32)cpu_to_be32(word);
++              crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word));
++      }
++
++      up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);
++      if (crc != up_crc) {
++              phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
++                         crc, up_crc);
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size,
++                     enum aqr_fw_src fw_src)
++{
++      u16 calculated_crc, read_crc, read_primary_offset;
++      u32 iram_offset = 0, iram_size = 0;
++      u32 dram_offset = 0, dram_size = 0;
++      char version[VERSION_STRING_SIZE];
++      u32 primary_offset = 0;
++      int ret;
++
++      /* extract saved CRC at the end of the fw
++       * CRC is saved in big-endian as PHY is BE
++       */
++      ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc);
++      if (ret) {
++              phydev_err(phydev, "bad firmware CRC in firmware\n");
++              return ret;
++      }
++      calculated_crc = crc_ccitt_false(0, data, size - sizeof(u16));
++      if (read_crc != calculated_crc) {
++              phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n",
++                         read_crc, calculated_crc);
++              return -EINVAL;
++      }
++
++      /* Get the primary offset to extract DRAM and IRAM sections. */
++      ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset);
++      if (ret) {
++              phydev_err(phydev, "bad primary offset in firmware\n");
++              return ret;
++      }
++      primary_offset = PRIMARY_OFFSET(read_primary_offset);
++
++      /* Find the DRAM and IRAM sections within the firmware file.
++       * Make sure the fw_header is correctly in the firmware.
++       */
++      if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET,
++                               sizeof(struct aqr_fw_header))) {
++              phydev_err(phydev, "bad fw_header in firmware\n");
++              return -EINVAL;
++      }
++
++      /* offset are in LE and values needs to be converted to cpu endian */
++      ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
++                            offsetof(struct aqr_fw_header, iram_offset),
++                            size, &iram_offset);
++      if (ret) {
++              phydev_err(phydev, "bad iram offset in firmware\n");
++              return ret;
++      }
++      ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
++                            offsetof(struct aqr_fw_header, iram_size),
++                            size, &iram_size);
++      if (ret) {
++              phydev_err(phydev, "invalid iram size in firmware\n");
++              return ret;
++      }
++      ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
++                            offsetof(struct aqr_fw_header, dram_offset),
++                            size, &dram_offset);
++      if (ret) {
++              phydev_err(phydev, "bad dram offset in firmware\n");
++              return ret;
++      }
++      ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
++                            offsetof(struct aqr_fw_header, dram_size),
++                            size, &dram_size);
++      if (ret) {
++              phydev_err(phydev, "invalid dram size in firmware\n");
++              return ret;
++      }
++
++      /* Increment the offset with the primary offset.
++       * Validate iram/dram offset and size.
++       */
++      iram_offset += primary_offset;
++      if (iram_size % sizeof(u32)) {
++              phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n");
++              return -EINVAL;
++      }
++      if (!aqr_fw_validate_get(size, iram_offset, iram_size)) {
++              phydev_err(phydev, "invalid iram offset for iram size\n");
++              return -EINVAL;
++      }
++
++      dram_offset += primary_offset;
++      if (dram_size % sizeof(u32)) {
++              phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n");
++              return -EINVAL;
++      }
++      if (!aqr_fw_validate_get(size, dram_offset, dram_size)) {
++              phydev_err(phydev, "invalid iram offset for iram size\n");
++              return -EINVAL;
++      }
++
++      phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n",
++                 primary_offset, iram_offset, iram_size, dram_offset, dram_size);
++
++      if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET,
++                               VERSION_STRING_SIZE)) {
++              phydev_err(phydev, "invalid version in firmware\n");
++              return -EINVAL;
++      }
++      strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET,
++              VERSION_STRING_SIZE);
++      if (version[0] == '\0') {
++              phydev_err(phydev, "invalid version in firmware\n");
++              return -EINVAL;
++      }
++      phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version,
++                  aqr_fw_src_string[fw_src]);
++
++      /* stall the microcprocessor */
++      phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
++                    VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
++
++      phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n",
++                 DRAM_BASE_ADDR, dram_offset, dram_size);
++      ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset,
++                               dram_size);
++      if (ret)
++              return ret;
++
++      phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n",
++                 IRAM_BASE_ADDR, iram_offset, iram_size);
++      ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset,
++                               iram_size);
++      if (ret)
++              return ret;
++
++      /* make sure soft reset and low power mode are clear */
++      phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC,
++                         VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER);
++
++      /* Release the microprocessor. UP_RESET must be held for 100 usec. */
++      phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
++                    VEND1_GLOBAL_CONTROL2_UP_RUN_STALL |
++                    VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD |
++                    VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST);
++      usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2);
++
++      phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
++                    VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
++
++      return 0;
++}
++
++static int aqr_firmware_load_nvmem(struct phy_device *phydev)
++{
++      struct nvmem_cell *cell;
++      size_t size;
++      u8 *buf;
++      int ret;
++
++      cell = nvmem_cell_get(&phydev->mdio.dev, "firmware");
++      if (IS_ERR(cell))
++              return PTR_ERR(cell);
++
++      buf = nvmem_cell_read(cell, &size);
++      if (IS_ERR(buf)) {
++              ret = PTR_ERR(buf);
++              goto exit;
++      }
++
++      ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM);
++      if (ret)
++              phydev_err(phydev, "firmware loading failed: %d\n", ret);
++
++      kfree(buf);
++exit:
++      nvmem_cell_put(cell);
++
++      return ret;
++}
++
++static int aqr_firmware_load_fs(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      const struct firmware *fw;
++      const char *fw_name;
++      int ret;
++
++      ret = of_property_read_string(dev->of_node, "firmware-name",
++                                    &fw_name);
++      if (ret)
++              return ret;
++
++      ret = request_firmware(&fw, fw_name, dev);
++      if (ret) {
++              phydev_err(phydev, "failed to find FW file %s (%d)\n",
++                         fw_name, ret);
++              return ret;
++      }
++
++      ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS);
++      if (ret)
++              phydev_err(phydev, "firmware loading failed: %d\n", ret);
++
++      release_firmware(fw);
++
++      return ret;
++}
++
++int aqr_firmware_load(struct phy_device *phydev)
++{
++      int ret;
++
++      /* Check if the firmware is not already loaded by pooling
++       * the current version returned by the PHY. If 0 is returned,
++       * no firmware is loaded.
++       */
++      ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
++      if (ret > 0)
++              goto exit;
++
++      ret = aqr_firmware_load_nvmem(phydev);
++      if (!ret)
++              goto exit;
++
++      ret = aqr_firmware_load_fs(phydev);
++      if (ret)
++              return ret;
++
++exit:
++      return 0;
++}
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -658,11 +658,17 @@ static int aqr107_resume(struct phy_devi
+ static int aqr107_probe(struct phy_device *phydev)
+ {
++      int ret;
++
+       phydev->priv = devm_kzalloc(&phydev->mdio.dev,
+                                   sizeof(struct aqr107_priv), GFP_KERNEL);
+       if (!phydev->priv)
+               return -ENOMEM;
++      ret = aqr_firmware_load(phydev);
++      if (ret)
++              return ret;
++
+       return aqr_hwmon_probe(phydev);
+ }
diff --git a/target/linux/generic/backport-6.6/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch b/target/linux/generic/backport-6.6/707-v6.8-02-net-phy-at803x-move-disable-WOL-to-specific-at8031-p.patch
new file mode 100644 (file)
index 0000000..eb9172b
--- /dev/null
@@ -0,0 +1,69 @@
+From 6a3b8c573b5a152a6aa7a0b54c5e18b84c6ba6f5 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:49 +0100
+Subject: [PATCH 02/13] net: phy: at803x: move disable WOL to specific at8031
+ probe
+
+Move the WOL disable call to specific at8031 probe to make at803x_probe
+more generic and drop extra check for PHY ID.
+
+Keep the same previous behaviour by first calling at803x_probe and then
+disabling WOL.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 27 +++++++++++++++++----------
+ 1 file changed, 17 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -886,15 +886,6 @@ static int at803x_probe(struct phy_devic
+                       priv->is_fiber = true;
+                       break;
+               }
+-
+-              /* Disable WoL in 1588 register which is enabled
+-               * by default
+-               */
+-              ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
+-                                   AT803X_PHY_MMD3_WOL_CTRL,
+-                                   AT803X_WOL_EN, 0);
+-              if (ret)
+-                      return ret;
+       }
+       return 0;
+@@ -1591,6 +1582,22 @@ static int at803x_cable_test_start(struc
+       return 0;
+ }
++static int at8031_probe(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = at803x_probe(phydev);
++      if (ret)
++              return ret;
++
++      /* Disable WoL in 1588 register which is enabled
++       * by default
++       */
++      return phy_modify_mmd(phydev, MDIO_MMD_PCS,
++                            AT803X_PHY_MMD3_WOL_CTRL,
++                            AT803X_WOL_EN, 0);
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+       u8 switch_revision;
+@@ -2092,7 +2099,7 @@ static struct phy_driver at803x_driver[]
+       PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
+       .name                   = "Qualcomm Atheros AR8031/AR8033",
+       .flags                  = PHY_POLL_CABLE_TEST,
+-      .probe                  = at803x_probe,
++      .probe                  = at8031_probe,
+       .config_init            = at803x_config_init,
+       .config_aneg            = at803x_config_aneg,
+       .soft_reset             = genphy_soft_reset,
diff --git a/target/linux/generic/backport-6.6/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch b/target/linux/generic/backport-6.6/707-v6.8-03-net-phy-at803x-raname-hw_stats-functions-to-qca83xx-.patch
new file mode 100644 (file)
index 0000000..5a9d176
--- /dev/null
@@ -0,0 +1,129 @@
+From 07b1ad83b9ed6db1735ba10baf67b7a565ac0cef Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:50 +0100
+Subject: [PATCH 03/13] net: phy: at803x: raname hw_stats functions to qca83xx
+ specific name
+
+The function and the struct related to hw_stats were specific to qca83xx
+PHY but were called following the convention in the driver of calling
+everything with at803x prefix.
+
+To better organize the code, rename these function a more specific name
+to better describe that they are specific to 83xx PHY family.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 44 ++++++++++++++++++++--------------------
+ 1 file changed, 22 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -295,7 +295,7 @@ struct at803x_hw_stat {
+       enum stat_access_type access_type;
+ };
+-static struct at803x_hw_stat at803x_hw_stats[] = {
++static struct at803x_hw_stat qca83xx_hw_stats[] = {
+       { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
+       { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
+       { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
+@@ -311,7 +311,7 @@ struct at803x_priv {
+       bool is_1000basex;
+       struct regulator_dev *vddio_rdev;
+       struct regulator_dev *vddh_rdev;
+-      u64 stats[ARRAY_SIZE(at803x_hw_stats)];
++      u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
+ };
+ struct at803x_context {
+@@ -529,24 +529,24 @@ static void at803x_get_wol(struct phy_de
+               wol->wolopts |= WAKE_MAGIC;
+ }
+-static int at803x_get_sset_count(struct phy_device *phydev)
++static int qca83xx_get_sset_count(struct phy_device *phydev)
+ {
+-      return ARRAY_SIZE(at803x_hw_stats);
++      return ARRAY_SIZE(qca83xx_hw_stats);
+ }
+-static void at803x_get_strings(struct phy_device *phydev, u8 *data)
++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
+ {
+       int i;
+-      for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) {
++      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
+               strscpy(data + i * ETH_GSTRING_LEN,
+-                      at803x_hw_stats[i].string, ETH_GSTRING_LEN);
++                      qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
+       }
+ }
+-static u64 at803x_get_stat(struct phy_device *phydev, int i)
++static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
+ {
+-      struct at803x_hw_stat stat = at803x_hw_stats[i];
++      struct at803x_hw_stat stat = qca83xx_hw_stats[i];
+       struct at803x_priv *priv = phydev->priv;
+       int val;
+       u64 ret;
+@@ -567,13 +567,13 @@ static u64 at803x_get_stat(struct phy_de
+       return ret;
+ }
+-static void at803x_get_stats(struct phy_device *phydev,
+-                           struct ethtool_stats *stats, u64 *data)
++static void qca83xx_get_stats(struct phy_device *phydev,
++                            struct ethtool_stats *stats, u64 *data)
+ {
+       int i;
+-      for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++)
+-              data[i] = at803x_get_stat(phydev, i);
++      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
++              data[i] = qca83xx_get_stat(phydev, i);
+ }
+ static int at803x_suspend(struct phy_device *phydev)
+@@ -2175,9 +2175,9 @@ static struct phy_driver at803x_driver[]
+       .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,
++      .get_sset_count         = qca83xx_get_sset_count,
++      .get_strings            = qca83xx_get_strings,
++      .get_stats              = qca83xx_get_stats,
+       .suspend                = qca83xx_suspend,
+       .resume                 = qca83xx_resume,
+ }, {
+@@ -2191,9 +2191,9 @@ static struct phy_driver at803x_driver[]
+       .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,
++      .get_sset_count         = qca83xx_get_sset_count,
++      .get_strings            = qca83xx_get_strings,
++      .get_stats              = qca83xx_get_stats,
+       .suspend                = qca83xx_suspend,
+       .resume                 = qca83xx_resume,
+ }, {
+@@ -2207,9 +2207,9 @@ static struct phy_driver at803x_driver[]
+       .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,
++      .get_sset_count         = qca83xx_get_sset_count,
++      .get_strings            = qca83xx_get_strings,
++      .get_stats              = qca83xx_get_stats,
+       .suspend                = qca83xx_suspend,
+       .resume                 = qca83xx_resume,
+ }, {
diff --git a/target/linux/generic/backport-6.6/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch b/target/linux/generic/backport-6.6/707-v6.8-04-net-phy-at803x-move-qca83xx-specific-check-in-dedica.patch
new file mode 100644 (file)
index 0000000..f09142c
--- /dev/null
@@ -0,0 +1,155 @@
+From d43cff3f82336c0bd965ea552232d9f4ddac71a6 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:51 +0100
+Subject: [PATCH 04/13] net: phy: at803x: move qca83xx specific check in
+ dedicated functions
+
+Rework qca83xx specific check to dedicated function to tidy things up
+and drop useless phy_id check.
+
+Also drop an useless link_change_notify for QCA8337 as it did nothing an
+returned early.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 68 ++++++++++++++++++++++------------------
+ 1 file changed, 37 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1623,27 +1623,26 @@ static int qca83xx_config_init(struct ph
+               break;
+       }
++      /* Following original QCA sourcecode set port to prefer master */
++      phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
++
++      return 0;
++}
++
++static int qca8327_config_init(struct phy_device *phydev)
++{
+       /* 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_ANALOG_TEST_CTRL,
+-                                    QCA8327_DEBUG_MANU_CTRL_EN, 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 */
+-      phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
+-
+-      return 0;
++      return qca83xx_config_init(phydev);
+ }
+ 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)
+@@ -1686,19 +1685,6 @@ static int qca83xx_resume(struct phy_dev
+ 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_GREEN,
+                             AT803X_DEBUG_GATE_CLK_IN1000, 0);
+@@ -1709,6 +1695,27 @@ static int qca83xx_suspend(struct phy_de
+       return 0;
+ }
++static int qca8337_suspend(struct phy_device *phydev)
++{
++      /* Only QCA8337 support actual suspend. */
++      genphy_suspend(phydev);
++
++      return qca83xx_suspend(phydev);
++}
++
++static int qca8327_suspend(struct phy_device *phydev)
++{
++      u16 mask = 0;
++
++      /* QCA8327 cause port unreliability when phy suspend
++       * is set.
++       */
++      mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
++      phy_modify(phydev, MII_BMCR, mask, 0);
++
++      return qca83xx_suspend(phydev);
++}
++
+ static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
+ {
+       int ret;
+@@ -2170,7 +2177,6 @@ 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,
+@@ -2178,7 +2184,7 @@ static struct phy_driver at803x_driver[]
+       .get_sset_count         = qca83xx_get_sset_count,
+       .get_strings            = qca83xx_get_strings,
+       .get_stats              = qca83xx_get_stats,
+-      .suspend                = qca83xx_suspend,
++      .suspend                = qca8337_suspend,
+       .resume                 = qca83xx_resume,
+ }, {
+       /* QCA8327-A from switch QCA8327-AL1A */
+@@ -2189,12 +2195,12 @@ static struct phy_driver at803x_driver[]
+       .link_change_notify     = qca83xx_link_change_notify,
+       .probe                  = at803x_probe,
+       .flags                  = PHY_IS_INTERNAL,
+-      .config_init            = qca83xx_config_init,
++      .config_init            = qca8327_config_init,
+       .soft_reset             = genphy_soft_reset,
+       .get_sset_count         = qca83xx_get_sset_count,
+       .get_strings            = qca83xx_get_strings,
+       .get_stats              = qca83xx_get_stats,
+-      .suspend                = qca83xx_suspend,
++      .suspend                = qca8327_suspend,
+       .resume                 = qca83xx_resume,
+ }, {
+       /* QCA8327-B from switch QCA8327-BL1A */
+@@ -2205,12 +2211,12 @@ static struct phy_driver at803x_driver[]
+       .link_change_notify     = qca83xx_link_change_notify,
+       .probe                  = at803x_probe,
+       .flags                  = PHY_IS_INTERNAL,
+-      .config_init            = qca83xx_config_init,
++      .config_init            = qca8327_config_init,
+       .soft_reset             = genphy_soft_reset,
+       .get_sset_count         = qca83xx_get_sset_count,
+       .get_strings            = qca83xx_get_strings,
+       .get_stats              = qca83xx_get_stats,
+-      .suspend                = qca83xx_suspend,
++      .suspend                = qca8327_suspend,
+       .resume                 = qca83xx_resume,
+ }, {
+       /* Qualcomm QCA8081 */
diff --git a/target/linux/generic/backport-6.6/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch b/target/linux/generic/backport-6.6/707-v6.8-05-net-phy-at803x-move-specific-DT-option-for-at8031-to.patch
new file mode 100644 (file)
index 0000000..a5cc67a
--- /dev/null
@@ -0,0 +1,94 @@
+From 900eef75cc5018e149c52fe305c9c3fe424c52a7 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:52 +0100
+Subject: [PATCH 05/13] net: phy: at803x: move specific DT option for at8031 to
+ specific probe
+
+Move specific DT options for at8031 to specific probe to tidy things up
+and make at803x_parse_dt more generic.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 55 ++++++++++++++++++++++------------------
+ 1 file changed, 31 insertions(+), 24 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -825,30 +825,6 @@ static int at803x_parse_dt(struct phy_de
+               }
+       }
+-      /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
+-       * options.
+-       */
+-      if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+-              if (of_property_read_bool(node, "qca,keep-pll-enabled"))
+-                      priv->flags |= AT803X_KEEP_PLL_ENABLED;
+-
+-              ret = at8031_register_regulators(phydev);
+-              if (ret < 0)
+-                      return ret;
+-
+-              ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
+-                                                       "vddio");
+-              if (ret) {
+-                      phydev_err(phydev, "failed to get VDDIO regulator\n");
+-                      return ret;
+-              }
+-
+-              /* Only AR8031/8033 support 1000Base-X for SFP modules */
+-              ret = phy_sfp_probe(phydev, &at803x_sfp_ops);
+-              if (ret < 0)
+-                      return ret;
+-      }
+-
+       return 0;
+ }
+@@ -1582,6 +1558,30 @@ static int at803x_cable_test_start(struc
+       return 0;
+ }
++static int at8031_parse_dt(struct phy_device *phydev)
++{
++      struct device_node *node = phydev->mdio.dev.of_node;
++      struct at803x_priv *priv = phydev->priv;
++      int ret;
++
++      if (of_property_read_bool(node, "qca,keep-pll-enabled"))
++              priv->flags |= AT803X_KEEP_PLL_ENABLED;
++
++      ret = at8031_register_regulators(phydev);
++      if (ret < 0)
++              return ret;
++
++      ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
++                                               "vddio");
++      if (ret) {
++              phydev_err(phydev, "failed to get VDDIO regulator\n");
++              return ret;
++      }
++
++      /* Only AR8031/8033 support 1000Base-X for SFP modules */
++      return phy_sfp_probe(phydev, &at803x_sfp_ops);
++}
++
+ static int at8031_probe(struct phy_device *phydev)
+ {
+       int ret;
+@@ -1590,6 +1590,13 @@ static int at8031_probe(struct phy_devic
+       if (ret)
+               return ret;
++      /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
++       * options.
++       */
++      ret = at8031_parse_dt(phydev);
++      if (ret)
++              return ret;
++
+       /* Disable WoL in 1588 register which is enabled
+        * by default
+        */
diff --git a/target/linux/generic/backport-6.6/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch b/target/linux/generic/backport-6.6/707-v6.8-06-net-phy-at803x-move-specific-at8031-probe-mode-check.patch
new file mode 100644 (file)
index 0000000..9e10e00
--- /dev/null
@@ -0,0 +1,78 @@
+From 25d2ba94005fac18fe68878cddff59a67e115554 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:53 +0100
+Subject: [PATCH 06/13] net: phy: at803x: move specific at8031 probe mode check
+ to dedicated probe
+
+Move specific at8031 probe mode check to dedicated probe to make
+at803x_probe more generic and keep code tidy.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 39 +++++++++++++++++++--------------------
+ 1 file changed, 19 insertions(+), 20 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -844,26 +844,6 @@ static int at803x_probe(struct phy_devic
+       if (ret)
+               return ret;
+-      if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+-              int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
+-              int mode_cfg;
+-
+-              if (ccr < 0)
+-                      return ccr;
+-              mode_cfg = ccr & AT803X_MODE_CFG_MASK;
+-
+-              switch (mode_cfg) {
+-              case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
+-              case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
+-                      priv->is_1000basex = true;
+-                      fallthrough;
+-              case AT803X_MODE_CFG_FX100_RGMII_50OHM:
+-              case AT803X_MODE_CFG_FX100_RGMII_75OHM:
+-                      priv->is_fiber = true;
+-                      break;
+-              }
+-      }
+-
+       return 0;
+ }
+@@ -1584,6 +1564,9 @@ static int at8031_parse_dt(struct phy_de
+ static int at8031_probe(struct phy_device *phydev)
+ {
++      struct at803x_priv *priv = phydev->priv;
++      int mode_cfg;
++      int ccr;
+       int ret;
+       ret = at803x_probe(phydev);
+@@ -1597,6 +1580,22 @@ static int at8031_probe(struct phy_devic
+       if (ret)
+               return ret;
++      ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
++      if (ccr < 0)
++              return ccr;
++      mode_cfg = ccr & AT803X_MODE_CFG_MASK;
++
++      switch (mode_cfg) {
++      case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
++      case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
++              priv->is_1000basex = true;
++              fallthrough;
++      case AT803X_MODE_CFG_FX100_RGMII_50OHM:
++      case AT803X_MODE_CFG_FX100_RGMII_75OHM:
++              priv->is_fiber = true;
++              break;
++      }
++
+       /* Disable WoL in 1588 register which is enabled
+        * by default
+        */
diff --git a/target/linux/generic/backport-6.6/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch b/target/linux/generic/backport-6.6/707-v6.8-07-net-phy-at803x-move-specific-at8031-config_init-to-d.patch
new file mode 100644 (file)
index 0000000..c9ef6df
--- /dev/null
@@ -0,0 +1,86 @@
+From 3ae3bc426eaf57ca8f53d75777d9a5ef779bc7b7 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:54 +0100
+Subject: [PATCH 07/13] net: phy: at803x: move specific at8031 config_init to
+ dedicated function
+
+Move specific at8031 config_init to dedicated function to make
+at803x_config_init more generic and tidy things up.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 45 ++++++++++++++++++++++------------------
+ 1 file changed, 25 insertions(+), 20 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -951,27 +951,8 @@ static int at803x_hibernation_mode_confi
+ static int at803x_config_init(struct phy_device *phydev)
+ {
+-      struct at803x_priv *priv = phydev->priv;
+       int ret;
+-      if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+-              /* Some bootloaders leave the fiber page selected.
+-               * Switch to the appropriate page (fiber or copper), as otherwise we
+-               * read the PHY capabilities from the wrong page.
+-               */
+-              phy_lock_mdio_bus(phydev);
+-              ret = at803x_write_page(phydev,
+-                                      priv->is_fiber ? AT803X_PAGE_FIBER :
+-                                                       AT803X_PAGE_COPPER);
+-              phy_unlock_mdio_bus(phydev);
+-              if (ret)
+-                      return ret;
+-
+-              ret = at8031_pll_config(phydev);
+-              if (ret < 0)
+-                      return ret;
+-      }
+-
+       /* The RX and TX delay default is:
+        *   after HW reset: RX delay enabled and TX delay disabled
+        *   after SW reset: RX delay enabled, while TX delay retains the
+@@ -1604,6 +1585,30 @@ static int at8031_probe(struct phy_devic
+                             AT803X_WOL_EN, 0);
+ }
++static int at8031_config_init(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      int ret;
++
++      /* Some bootloaders leave the fiber page selected.
++       * Switch to the appropriate page (fiber or copper), as otherwise we
++       * read the PHY capabilities from the wrong page.
++       */
++      phy_lock_mdio_bus(phydev);
++      ret = at803x_write_page(phydev,
++                              priv->is_fiber ? AT803X_PAGE_FIBER :
++                                               AT803X_PAGE_COPPER);
++      phy_unlock_mdio_bus(phydev);
++      if (ret)
++              return ret;
++
++      ret = at8031_pll_config(phydev);
++      if (ret < 0)
++              return ret;
++
++      return at803x_config_init(phydev);
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+       u8 switch_revision;
+@@ -2113,7 +2118,7 @@ static struct phy_driver at803x_driver[]
+       .name                   = "Qualcomm Atheros AR8031/AR8033",
+       .flags                  = PHY_POLL_CABLE_TEST,
+       .probe                  = at8031_probe,
+-      .config_init            = at803x_config_init,
++      .config_init            = at8031_config_init,
+       .config_aneg            = at803x_config_aneg,
+       .soft_reset             = genphy_soft_reset,
+       .set_wol                = at803x_set_wol,
diff --git a/target/linux/generic/backport-6.6/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch b/target/linux/generic/backport-6.6/707-v6.8-08-net-phy-at803x-move-specific-at8031-WOL-bits-to-dedi.patch
new file mode 100644 (file)
index 0000000..6746a4d
--- /dev/null
@@ -0,0 +1,92 @@
+From 27b89c9dc1b0393090d68d651b82f30ad2696baa Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:55 +0100
+Subject: [PATCH 08/13] net: phy: at803x: move specific at8031 WOL bits to
+ dedicated function
+
+Move specific at8031 WOL enable/disable to dedicated function to make
+at803x_set_wol more generic.
+
+This is needed in preparation for PHY driver split as qca8081 share the
+same function to toggle WOL settings.
+
+In this new implementation WOL module in at8031 is enabled after the
+generic interrupt is setup. This should not cause any problem as the
+WOL_INT has a separate implementation and only relay on MAC bits.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 42 ++++++++++++++++++++++++----------------
+ 1 file changed, 25 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -466,27 +466,11 @@ static int at803x_set_wol(struct phy_dev
+                       phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
+                                     mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
+-              /* Enable WOL function for 1588 */
+-              if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+-                      ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
+-                                           AT803X_PHY_MMD3_WOL_CTRL,
+-                                           0, AT803X_WOL_EN);
+-                      if (ret)
+-                              return ret;
+-              }
+               /* Enable WOL interrupt */
+               ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
+               if (ret)
+                       return ret;
+       } else {
+-              /* Disable WoL function for 1588 */
+-              if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+-                      ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
+-                                           AT803X_PHY_MMD3_WOL_CTRL,
+-                                           AT803X_WOL_EN, 0);
+-                      if (ret)
+-                              return ret;
+-              }
+               /* Disable WOL interrupt */
+               ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
+               if (ret)
+@@ -1609,6 +1593,30 @@ static int at8031_config_init(struct phy
+       return at803x_config_init(phydev);
+ }
++static int at8031_set_wol(struct phy_device *phydev,
++                        struct ethtool_wolinfo *wol)
++{
++      int ret;
++
++      /* First setup MAC address and enable WOL interrupt */
++      ret = at803x_set_wol(phydev, wol);
++      if (ret)
++              return ret;
++
++      if (wol->wolopts & WAKE_MAGIC)
++              /* Enable WOL function for 1588 */
++              ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
++                                   AT803X_PHY_MMD3_WOL_CTRL,
++                                   0, AT803X_WOL_EN);
++      else
++              /* Disable WoL function for 1588 */
++              ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
++                                   AT803X_PHY_MMD3_WOL_CTRL,
++                                   AT803X_WOL_EN, 0);
++
++      return ret;
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+       u8 switch_revision;
+@@ -2121,7 +2129,7 @@ static struct phy_driver at803x_driver[]
+       .config_init            = at8031_config_init,
+       .config_aneg            = at803x_config_aneg,
+       .soft_reset             = genphy_soft_reset,
+-      .set_wol                = at803x_set_wol,
++      .set_wol                = at8031_set_wol,
+       .get_wol                = at803x_get_wol,
+       .suspend                = at803x_suspend,
+       .resume                 = at803x_resume,
diff --git a/target/linux/generic/backport-6.6/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch b/target/linux/generic/backport-6.6/707-v6.8-09-net-phy-at803x-move-specific-at8031-config_intr-to-d.patch
new file mode 100644 (file)
index 0000000..0d284c3
--- /dev/null
@@ -0,0 +1,78 @@
+From 30dd62191d3dd97c08f7f9dc9ce77ffab457e4fb Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:56 +0100
+Subject: [PATCH 09/13] net: phy: at803x: move specific at8031 config_intr to
+ dedicated function
+
+Move specific at8031 config_intr bits to dedicated function to make
+at803x_config_initr more generic.
+
+This is needed in preparation for PHY driver split as qca8081 share the
+same function to setup interrupts.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 30 ++++++++++++++++++++++++------
+ 1 file changed, 24 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -990,7 +990,6 @@ static int at803x_ack_interrupt(struct p
+ static int at803x_config_intr(struct phy_device *phydev)
+ {
+-      struct at803x_priv *priv = phydev->priv;
+       int err;
+       int value;
+@@ -1007,10 +1006,6 @@ static int at803x_config_intr(struct phy
+               value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
+               value |= AT803X_INTR_ENABLE_LINK_FAIL;
+               value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
+-              if (priv->is_fiber) {
+-                      value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
+-                      value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
+-              }
+               err = phy_write(phydev, AT803X_INTR_ENABLE, value);
+       } else {
+@@ -1617,6 +1612,29 @@ static int at8031_set_wol(struct phy_dev
+       return ret;
+ }
++static int at8031_config_intr(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      int err, value = 0;
++
++      if (phydev->interrupts == PHY_INTERRUPT_ENABLED &&
++          priv->is_fiber) {
++              /* Clear any pending interrupts */
++              err = at803x_ack_interrupt(phydev);
++              if (err)
++                      return err;
++
++              value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
++              value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
++
++              err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value);
++              if (err)
++                      return err;
++      }
++
++      return at803x_config_intr(phydev);
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+       u8 switch_revision;
+@@ -2137,7 +2155,7 @@ static struct phy_driver at803x_driver[]
+       .write_page             = at803x_write_page,
+       .get_features           = at803x_get_features,
+       .read_status            = at803x_read_status,
+-      .config_intr            = at803x_config_intr,
++      .config_intr            = at8031_config_intr,
+       .handle_interrupt       = at803x_handle_interrupt,
+       .get_tunable            = at803x_get_tunable,
+       .set_tunable            = at803x_set_tunable,
diff --git a/target/linux/generic/backport-6.6/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch b/target/linux/generic/backport-6.6/707-v6.8-10-net-phy-at803x-make-at8031-related-DT-functions-name.patch
new file mode 100644 (file)
index 0000000..210949e
--- /dev/null
@@ -0,0 +1,78 @@
+From a5ab9d8e7ae0da8328ac1637a9755311508dc8ab Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:57 +0100
+Subject: [PATCH 10/13] net: phy: at803x: make at8031 related DT functions name
+ more specific
+
+Rename at8031 related DT function name to a more specific name
+referencing they are only related to at8031 and not to the generic
+at803x PHY family.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -583,7 +583,7 @@ static int at803x_resume(struct phy_devi
+       return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
+ }
+-static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
+                                           unsigned int selector)
+ {
+       struct phy_device *phydev = rdev_get_drvdata(rdev);
+@@ -596,7 +596,7 @@ static int at803x_rgmii_reg_set_voltage_
+                                            AT803X_DEBUG_RGMII_1V8, 0);
+ }
+-static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
+ {
+       struct phy_device *phydev = rdev_get_drvdata(rdev);
+       int val;
+@@ -610,8 +610,8 @@ static int at803x_rgmii_reg_get_voltage_
+ static const struct regulator_ops vddio_regulator_ops = {
+       .list_voltage = regulator_list_voltage_table,
+-      .set_voltage_sel = at803x_rgmii_reg_set_voltage_sel,
+-      .get_voltage_sel = at803x_rgmii_reg_get_voltage_sel,
++      .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
++      .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
+ };
+ static const unsigned int vddio_voltage_table[] = {
+@@ -666,7 +666,7 @@ static int at8031_register_regulators(st
+       return 0;
+ }
+-static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
+ {
+       struct phy_device *phydev = upstream;
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
+@@ -710,10 +710,10 @@ static int at803x_sfp_insert(void *upstr
+       return 0;
+ }
+-static const struct sfp_upstream_ops at803x_sfp_ops = {
++static const struct sfp_upstream_ops at8031_sfp_ops = {
+       .attach = phy_sfp_attach,
+       .detach = phy_sfp_detach,
+-      .module_insert = at803x_sfp_insert,
++      .module_insert = at8031_sfp_insert,
+ };
+ static int at803x_parse_dt(struct phy_device *phydev)
+@@ -1519,7 +1519,7 @@ static int at8031_parse_dt(struct phy_de
+       }
+       /* Only AR8031/8033 support 1000Base-X for SFP modules */
+-      return phy_sfp_probe(phydev, &at803x_sfp_ops);
++      return phy_sfp_probe(phydev, &at8031_sfp_ops);
+ }
+ static int at8031_probe(struct phy_device *phydev)
diff --git a/target/linux/generic/backport-6.6/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch b/target/linux/generic/backport-6.6/707-v6.8-11-net-phy-at803x-move-at8031-functions-in-dedicated-se.patch
new file mode 100644 (file)
index 0000000..5856b59
--- /dev/null
@@ -0,0 +1,297 @@
+From f932a6dc8bae0dae9645b5b1b4c65aed8a8acb2a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:58 +0100
+Subject: [PATCH 11/13] net: phy: at803x: move at8031 functions in dedicated
+ section
+
+Move at8031 functions in dedicated section with dedicated at8031
+parse_dt and probe.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 266 +++++++++++++++++++--------------------
+ 1 file changed, 133 insertions(+), 133 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -583,139 +583,6 @@ static int at803x_resume(struct phy_devi
+       return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
+ }
+-static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
+-                                          unsigned int selector)
+-{
+-      struct phy_device *phydev = rdev_get_drvdata(rdev);
+-
+-      if (selector)
+-              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+-                                           0, AT803X_DEBUG_RGMII_1V8);
+-      else
+-              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+-                                           AT803X_DEBUG_RGMII_1V8, 0);
+-}
+-
+-static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
+-{
+-      struct phy_device *phydev = rdev_get_drvdata(rdev);
+-      int val;
+-
+-      val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
+-      if (val < 0)
+-              return val;
+-
+-      return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
+-}
+-
+-static const struct regulator_ops vddio_regulator_ops = {
+-      .list_voltage = regulator_list_voltage_table,
+-      .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
+-      .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
+-};
+-
+-static const unsigned int vddio_voltage_table[] = {
+-      1500000,
+-      1800000,
+-};
+-
+-static const struct regulator_desc vddio_desc = {
+-      .name = "vddio",
+-      .of_match = of_match_ptr("vddio-regulator"),
+-      .n_voltages = ARRAY_SIZE(vddio_voltage_table),
+-      .volt_table = vddio_voltage_table,
+-      .ops = &vddio_regulator_ops,
+-      .type = REGULATOR_VOLTAGE,
+-      .owner = THIS_MODULE,
+-};
+-
+-static const struct regulator_ops vddh_regulator_ops = {
+-};
+-
+-static const struct regulator_desc vddh_desc = {
+-      .name = "vddh",
+-      .of_match = of_match_ptr("vddh-regulator"),
+-      .n_voltages = 1,
+-      .fixed_uV = 2500000,
+-      .ops = &vddh_regulator_ops,
+-      .type = REGULATOR_VOLTAGE,
+-      .owner = THIS_MODULE,
+-};
+-
+-static int at8031_register_regulators(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      struct device *dev = &phydev->mdio.dev;
+-      struct regulator_config config = { };
+-
+-      config.dev = dev;
+-      config.driver_data = phydev;
+-
+-      priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
+-      if (IS_ERR(priv->vddio_rdev)) {
+-              phydev_err(phydev, "failed to register VDDIO regulator\n");
+-              return PTR_ERR(priv->vddio_rdev);
+-      }
+-
+-      priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
+-      if (IS_ERR(priv->vddh_rdev)) {
+-              phydev_err(phydev, "failed to register VDDH regulator\n");
+-              return PTR_ERR(priv->vddh_rdev);
+-      }
+-
+-      return 0;
+-}
+-
+-static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
+-{
+-      struct phy_device *phydev = upstream;
+-      __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
+-      __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
+-      DECLARE_PHY_INTERFACE_MASK(interfaces);
+-      phy_interface_t iface;
+-
+-      linkmode_zero(phy_support);
+-      phylink_set(phy_support, 1000baseX_Full);
+-      phylink_set(phy_support, 1000baseT_Full);
+-      phylink_set(phy_support, Autoneg);
+-      phylink_set(phy_support, Pause);
+-      phylink_set(phy_support, Asym_Pause);
+-
+-      linkmode_zero(sfp_support);
+-      sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
+-      /* Some modules support 10G modes as well as others we support.
+-       * Mask out non-supported modes so the correct interface is picked.
+-       */
+-      linkmode_and(sfp_support, phy_support, sfp_support);
+-
+-      if (linkmode_empty(sfp_support)) {
+-              dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
+-              return -EINVAL;
+-      }
+-
+-      iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
+-
+-      /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
+-       * interface for use with SFP modules.
+-       * However, some copper modules detected as having a preferred SGMII
+-       * interface do default to and function in 1000Base-X mode, so just
+-       * print a warning and allow such modules, as they may have some chance
+-       * of working.
+-       */
+-      if (iface == PHY_INTERFACE_MODE_SGMII)
+-              dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
+-      else if (iface != PHY_INTERFACE_MODE_1000BASEX)
+-              return -EINVAL;
+-
+-      return 0;
+-}
+-
+-static const struct sfp_upstream_ops at8031_sfp_ops = {
+-      .attach = phy_sfp_attach,
+-      .detach = phy_sfp_detach,
+-      .module_insert = at8031_sfp_insert,
+-};
+-
+ static int at803x_parse_dt(struct phy_device *phydev)
+ {
+       struct device_node *node = phydev->mdio.dev.of_node;
+@@ -1498,6 +1365,139 @@ static int at803x_cable_test_start(struc
+       return 0;
+ }
++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
++                                          unsigned int selector)
++{
++      struct phy_device *phydev = rdev_get_drvdata(rdev);
++
++      if (selector)
++              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
++                                           0, AT803X_DEBUG_RGMII_1V8);
++      else
++              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
++                                           AT803X_DEBUG_RGMII_1V8, 0);
++}
++
++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
++{
++      struct phy_device *phydev = rdev_get_drvdata(rdev);
++      int val;
++
++      val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
++      if (val < 0)
++              return val;
++
++      return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
++}
++
++static const struct regulator_ops vddio_regulator_ops = {
++      .list_voltage = regulator_list_voltage_table,
++      .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
++      .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
++};
++
++static const unsigned int vddio_voltage_table[] = {
++      1500000,
++      1800000,
++};
++
++static const struct regulator_desc vddio_desc = {
++      .name = "vddio",
++      .of_match = of_match_ptr("vddio-regulator"),
++      .n_voltages = ARRAY_SIZE(vddio_voltage_table),
++      .volt_table = vddio_voltage_table,
++      .ops = &vddio_regulator_ops,
++      .type = REGULATOR_VOLTAGE,
++      .owner = THIS_MODULE,
++};
++
++static const struct regulator_ops vddh_regulator_ops = {
++};
++
++static const struct regulator_desc vddh_desc = {
++      .name = "vddh",
++      .of_match = of_match_ptr("vddh-regulator"),
++      .n_voltages = 1,
++      .fixed_uV = 2500000,
++      .ops = &vddh_regulator_ops,
++      .type = REGULATOR_VOLTAGE,
++      .owner = THIS_MODULE,
++};
++
++static int at8031_register_regulators(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      struct device *dev = &phydev->mdio.dev;
++      struct regulator_config config = { };
++
++      config.dev = dev;
++      config.driver_data = phydev;
++
++      priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
++      if (IS_ERR(priv->vddio_rdev)) {
++              phydev_err(phydev, "failed to register VDDIO regulator\n");
++              return PTR_ERR(priv->vddio_rdev);
++      }
++
++      priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
++      if (IS_ERR(priv->vddh_rdev)) {
++              phydev_err(phydev, "failed to register VDDH regulator\n");
++              return PTR_ERR(priv->vddh_rdev);
++      }
++
++      return 0;
++}
++
++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++{
++      struct phy_device *phydev = upstream;
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
++      DECLARE_PHY_INTERFACE_MASK(interfaces);
++      phy_interface_t iface;
++
++      linkmode_zero(phy_support);
++      phylink_set(phy_support, 1000baseX_Full);
++      phylink_set(phy_support, 1000baseT_Full);
++      phylink_set(phy_support, Autoneg);
++      phylink_set(phy_support, Pause);
++      phylink_set(phy_support, Asym_Pause);
++
++      linkmode_zero(sfp_support);
++      sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
++      /* Some modules support 10G modes as well as others we support.
++       * Mask out non-supported modes so the correct interface is picked.
++       */
++      linkmode_and(sfp_support, phy_support, sfp_support);
++
++      if (linkmode_empty(sfp_support)) {
++              dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
++              return -EINVAL;
++      }
++
++      iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
++
++      /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
++       * interface for use with SFP modules.
++       * However, some copper modules detected as having a preferred SGMII
++       * interface do default to and function in 1000Base-X mode, so just
++       * print a warning and allow such modules, as they may have some chance
++       * of working.
++       */
++      if (iface == PHY_INTERFACE_MODE_SGMII)
++              dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
++      else if (iface != PHY_INTERFACE_MODE_1000BASEX)
++              return -EINVAL;
++
++      return 0;
++}
++
++static const struct sfp_upstream_ops at8031_sfp_ops = {
++      .attach = phy_sfp_attach,
++      .detach = phy_sfp_detach,
++      .module_insert = at8031_sfp_insert,
++};
++
+ static int at8031_parse_dt(struct phy_device *phydev)
+ {
+       struct device_node *node = phydev->mdio.dev.of_node;
diff --git a/target/linux/generic/backport-6.6/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch b/target/linux/generic/backport-6.6/707-v6.8-12-net-phy-at803x-move-at8035-specific-DT-parse-to-dedi.patch
new file mode 100644 (file)
index 0000000..0a822c6
--- /dev/null
@@ -0,0 +1,114 @@
+From 21a2802a8365cfa82cc02187c1f95136d85592ad Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:51:59 +0100
+Subject: [PATCH 12/13] net: phy: at803x: move at8035 specific DT parse to
+ dedicated probe
+
+Move at8035 specific DT parse for clock out frequency to dedicated probe
+to make at803x probe function more generic.
+
+This is to tidy code and no behaviour change are intended.
+
+Detection logic is changed, we check if the clk 25m mask is set and if
+it's not zero, we assume the qca,clk-out-frequency property is set.
+
+The property is checked in the generic at803x_parse_dt called by
+at803x_probe.
+
+Signed-off-by: Christian Marangi <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, 41 insertions(+), 19 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -638,23 +638,6 @@ static int at803x_parse_dt(struct phy_de
+               priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
+               priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
+-
+-              /* Fixup for the AR8030/AR8035. This chip has another mask and
+-               * doesn't support the DSP reference. Eg. the lowest bit of the
+-               * mask. The upper two bits select the same frequencies. Mask
+-               * the lowest bit here.
+-               *
+-               * Warning:
+-               *   There was no datasheet for the AR8030 available so this is
+-               *   just a guess. But the AR8035 is listed as pin compatible
+-               *   to the AR8030 so there might be a good chance it works on
+-               *   the AR8030 too.
+-               */
+-              if (phydev->drv->phy_id == ATH8030_PHY_ID ||
+-                  phydev->drv->phy_id == ATH8035_PHY_ID) {
+-                      priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
+-                      priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
+-              }
+       }
+       ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
+@@ -1635,6 +1618,45 @@ static int at8031_config_intr(struct phy
+       return at803x_config_intr(phydev);
+ }
++static int at8035_parse_dt(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++
++      /* Mask is set by the generic at803x_parse_dt
++       * if property is set. Assume property is set
++       * with the mask not zero.
++       */
++      if (priv->clk_25m_mask) {
++              /* Fixup for the AR8030/AR8035. This chip has another mask and
++               * doesn't support the DSP reference. Eg. the lowest bit of the
++               * mask. The upper two bits select the same frequencies. Mask
++               * the lowest bit here.
++               *
++               * Warning:
++               *   There was no datasheet for the AR8030 available so this is
++               *   just a guess. But the AR8035 is listed as pin compatible
++               *   to the AR8030 so there might be a good chance it works on
++               *   the AR8030 too.
++               */
++              priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
++              priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
++      }
++
++      return 0;
++}
++
++/* AR8030 and AR8035 shared the same special mask for clk_25m */
++static int at8035_probe(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = at803x_probe(phydev);
++      if (ret)
++              return ret;
++
++      return at8035_parse_dt(phydev);
++}
++
+ static int qca83xx_config_init(struct phy_device *phydev)
+ {
+       u8 switch_revision;
+@@ -2107,7 +2129,7 @@ static struct phy_driver at803x_driver[]
+       PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
+       .name                   = "Qualcomm Atheros AR8035",
+       .flags                  = PHY_POLL_CABLE_TEST,
+-      .probe                  = at803x_probe,
++      .probe                  = at8035_probe,
+       .config_aneg            = at803x_config_aneg,
+       .config_init            = at803x_config_init,
+       .soft_reset             = genphy_soft_reset,
+@@ -2128,7 +2150,7 @@ static struct phy_driver at803x_driver[]
+       .phy_id                 = ATH8030_PHY_ID,
+       .name                   = "Qualcomm Atheros AR8030",
+       .phy_id_mask            = AT8030_PHY_ID_MASK,
+-      .probe                  = at803x_probe,
++      .probe                  = at8035_probe,
+       .config_init            = at803x_config_init,
+       .link_change_notify     = at803x_link_change_notify,
+       .set_wol                = at803x_set_wol,
diff --git a/target/linux/generic/backport-6.6/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch b/target/linux/generic/backport-6.6/707-v6.8-13-net-phy-at803x-drop-specific-PHY-ID-check-from-cable.patch
new file mode 100644 (file)
index 0000000..e1ba6ec
--- /dev/null
@@ -0,0 +1,219 @@
+From ef9df47b449e32e06501a11272809be49019bdb6 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 8 Dec 2023 15:52:00 +0100
+Subject: [PATCH 13/13] net: phy: at803x: drop specific PHY ID check from cable
+ test functions
+
+Drop specific PHY ID check for cable test functions for at803x. This is
+done to make functions more generic. While at it better describe what
+the functions does by using more symbolic function names.
+
+PHYs that requires to set additional reg are moved to specific function
+calling the more generic one.
+
+cdt_start and cdt_wait_for_completion are changed to take an additional
+arg to pass specific values specific to the PHY.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 95 +++++++++++++++++++++-------------------
+ 1 file changed, 50 insertions(+), 45 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1222,31 +1222,16 @@ static int at803x_cdt_fault_length(u16 s
+       return (dt * 824) / 10;
+ }
+-static int at803x_cdt_start(struct phy_device *phydev, int pair)
++static int at803x_cdt_start(struct phy_device *phydev,
++                          u32 cdt_start)
+ {
+-      u16 cdt;
+-
+-      /* qca8081 takes the different bit 15 to enable CDT test */
+-      if (phydev->drv->phy_id == QCA8081_PHY_ID)
+-              cdt = QCA808X_CDT_ENABLE_TEST |
+-                      QCA808X_CDT_LENGTH_UNIT |
+-                      QCA808X_CDT_INTER_CHECK_DIS;
+-      else
+-              cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
+-                      AT803X_CDT_ENABLE_TEST;
+-
+-      return phy_write(phydev, AT803X_CDT, cdt);
++      return phy_write(phydev, AT803X_CDT, cdt_start);
+ }
+-static int at803x_cdt_wait_for_completion(struct phy_device *phydev)
++static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
++                                        u32 cdt_en)
+ {
+       int val, ret;
+-      u16 cdt_en;
+-
+-      if (phydev->drv->phy_id == QCA8081_PHY_ID)
+-              cdt_en = QCA808X_CDT_ENABLE_TEST;
+-      else
+-              cdt_en = AT803X_CDT_ENABLE_TEST;
+       /* One test run takes about 25ms */
+       ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
+@@ -1266,11 +1251,13 @@ static int at803x_cable_test_one_pair(st
+       };
+       int ret, val;
+-      ret = at803x_cdt_start(phydev, pair);
++      val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
++            AT803X_CDT_ENABLE_TEST;
++      ret = at803x_cdt_start(phydev, val);
+       if (ret)
+               return ret;
+-      ret = at803x_cdt_wait_for_completion(phydev);
++      ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST);
+       if (ret)
+               return ret;
+@@ -1292,19 +1279,11 @@ static int at803x_cable_test_one_pair(st
+ }
+ static int at803x_cable_test_get_status(struct phy_device *phydev,
+-                                      bool *finished)
++                                      bool *finished, unsigned long pair_mask)
+ {
+-      unsigned long pair_mask;
+       int retries = 20;
+       int pair, ret;
+-      if (phydev->phy_id == ATH9331_PHY_ID ||
+-          phydev->phy_id == ATH8032_PHY_ID ||
+-          phydev->phy_id == QCA9561_PHY_ID)
+-              pair_mask = 0x3;
+-      else
+-              pair_mask = 0xf;
+-
+       *finished = false;
+       /* According to the datasheet the CDT can be performed when
+@@ -1331,7 +1310,7 @@ static int at803x_cable_test_get_status(
+       return 0;
+ }
+-static int at803x_cable_test_start(struct phy_device *phydev)
++static void at803x_cable_test_autoneg(struct phy_device *phydev)
+ {
+       /* Enable auto-negotiation, but advertise no capabilities, no link
+        * will be established. A restart of the auto-negotiation is not
+@@ -1339,11 +1318,11 @@ static int at803x_cable_test_start(struc
+        */
+       phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
+       phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
+-      if (phydev->phy_id != ATH9331_PHY_ID &&
+-          phydev->phy_id != ATH8032_PHY_ID &&
+-          phydev->phy_id != QCA9561_PHY_ID)
+-              phy_write(phydev, MII_CTRL1000, 0);
++}
++static int at803x_cable_test_start(struct phy_device *phydev)
++{
++      at803x_cable_test_autoneg(phydev);
+       /* we do all the (time consuming) work later */
+       return 0;
+ }
+@@ -1618,6 +1597,29 @@ static int at8031_config_intr(struct phy
+       return at803x_config_intr(phydev);
+ }
++/* AR8031 and AR8035 share the same cable test get status reg */
++static int at8031_cable_test_get_status(struct phy_device *phydev,
++                                      bool *finished)
++{
++      return at803x_cable_test_get_status(phydev, finished, 0xf);
++}
++
++/* AR8031 and AR8035 share the same cable test start logic */
++static int at8031_cable_test_start(struct phy_device *phydev)
++{
++      at803x_cable_test_autoneg(phydev);
++      phy_write(phydev, MII_CTRL1000, 0);
++      /* we do all the (time consuming) work later */
++      return 0;
++}
++
++/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */
++static int at8032_cable_test_get_status(struct phy_device *phydev,
++                                      bool *finished)
++{
++      return at803x_cable_test_get_status(phydev, finished, 0x3);
++}
++
+ static int at8035_parse_dt(struct phy_device *phydev)
+ {
+       struct at803x_priv *priv = phydev->priv;
+@@ -2041,11 +2043,14 @@ static int qca808x_cable_test_get_status
+       *finished = false;
+-      ret = at803x_cdt_start(phydev, 0);
++      val = QCA808X_CDT_ENABLE_TEST |
++            QCA808X_CDT_LENGTH_UNIT |
++            QCA808X_CDT_INTER_CHECK_DIS;
++      ret = at803x_cdt_start(phydev, val);
+       if (ret)
+               return ret;
+-      ret = at803x_cdt_wait_for_completion(phydev);
++      ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
+       if (ret)
+               return ret;
+@@ -2143,8 +2148,8 @@ static struct phy_driver at803x_driver[]
+       .handle_interrupt       = at803x_handle_interrupt,
+       .get_tunable            = at803x_get_tunable,
+       .set_tunable            = at803x_set_tunable,
+-      .cable_test_start       = at803x_cable_test_start,
+-      .cable_test_get_status  = at803x_cable_test_get_status,
++      .cable_test_start       = at8031_cable_test_start,
++      .cable_test_get_status  = at8031_cable_test_get_status,
+ }, {
+       /* Qualcomm Atheros AR8030 */
+       .phy_id                 = ATH8030_PHY_ID,
+@@ -2181,8 +2186,8 @@ static struct phy_driver at803x_driver[]
+       .handle_interrupt       = at803x_handle_interrupt,
+       .get_tunable            = at803x_get_tunable,
+       .set_tunable            = at803x_set_tunable,
+-      .cable_test_start       = at803x_cable_test_start,
+-      .cable_test_get_status  = at803x_cable_test_get_status,
++      .cable_test_start       = at8031_cable_test_start,
++      .cable_test_get_status  = at8031_cable_test_get_status,
+ }, {
+       /* Qualcomm Atheros AR8032 */
+       PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
+@@ -2197,7 +2202,7 @@ static struct phy_driver at803x_driver[]
+       .config_intr            = at803x_config_intr,
+       .handle_interrupt       = at803x_handle_interrupt,
+       .cable_test_start       = at803x_cable_test_start,
+-      .cable_test_get_status  = at803x_cable_test_get_status,
++      .cable_test_get_status  = at8032_cable_test_get_status,
+ }, {
+       /* ATHEROS AR9331 */
+       PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
+@@ -2210,7 +2215,7 @@ static struct phy_driver at803x_driver[]
+       .config_intr            = at803x_config_intr,
+       .handle_interrupt       = at803x_handle_interrupt,
+       .cable_test_start       = at803x_cable_test_start,
+-      .cable_test_get_status  = at803x_cable_test_get_status,
++      .cable_test_get_status  = at8032_cable_test_get_status,
+       .read_status            = at803x_read_status,
+       .soft_reset             = genphy_soft_reset,
+       .config_aneg            = at803x_config_aneg,
+@@ -2226,7 +2231,7 @@ static struct phy_driver at803x_driver[]
+       .config_intr            = at803x_config_intr,
+       .handle_interrupt       = at803x_handle_interrupt,
+       .cable_test_start       = at803x_cable_test_start,
+-      .cable_test_get_status  = at803x_cable_test_get_status,
++      .cable_test_get_status  = at8032_cable_test_get_status,
+       .read_status            = at803x_read_status,
+       .soft_reset             = genphy_soft_reset,
+       .config_aneg            = at803x_config_aneg,
diff --git a/target/linux/generic/backport-6.6/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch b/target/linux/generic/backport-6.6/708-v6.8-01-net-phy-at803x-move-specific-qca808x-config_aneg-to-.patch
new file mode 100644 (file)
index 0000000..0bb3673
--- /dev/null
@@ -0,0 +1,116 @@
+From 8e732f1c6f2dc5e18f766d0f1b11df9db2dd044a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 14 Dec 2023 01:44:31 +0100
+Subject: [PATCH 1/2] net: phy: at803x: move specific qca808x config_aneg to
+ dedicated function
+
+Move specific qca808x config_aneg to dedicated function to permit easier
+split of qca808x portion from at803x driver.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 66 ++++++++++++++++++++++++----------------
+ 1 file changed, 40 insertions(+), 26 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1045,9 +1045,8 @@ static int at803x_config_mdix(struct phy
+                         FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
+ }
+-static int at803x_config_aneg(struct phy_device *phydev)
++static int at803x_prepare_config_aneg(struct phy_device *phydev)
+ {
+-      struct at803x_priv *priv = phydev->priv;
+       int ret;
+       ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
+@@ -1064,33 +1063,22 @@ static int at803x_config_aneg(struct phy
+                       return ret;
+       }
+-      if (priv->is_1000basex)
+-              return genphy_c37_config_aneg(phydev);
+-
+-      /* Do not restart auto-negotiation by setting ret to 0 defautly,
+-       * when calling __genphy_config_aneg later.
+-       */
+-      ret = 0;
+-
+-      if (phydev->drv->phy_id == QCA8081_PHY_ID) {
+-              int phy_ctrl = 0;
++      return 0;
++}
+-              /* The reg MII_BMCR also needs to be configured for force mode, the
+-               * genphy_config_aneg is also needed.
+-               */
+-              if (phydev->autoneg == AUTONEG_DISABLE)
+-                      genphy_c45_pma_setup_forced(phydev);
++static int at803x_config_aneg(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      int ret;
+-              if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
+-                      phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
++      ret = at803x_prepare_config_aneg(phydev);
++      if (ret)
++              return ret;
+-              ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+-                              MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
+-              if (ret < 0)
+-                      return ret;
+-      }
++      if (priv->is_1000basex)
++              return genphy_c37_config_aneg(phydev);
+-      return __genphy_config_aneg(phydev, ret);
++      return genphy_config_aneg(phydev);
+ }
+ static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
+@@ -2118,6 +2106,32 @@ static int qca808x_get_features(struct p
+       return 0;
+ }
++static int qca808x_config_aneg(struct phy_device *phydev)
++{
++      int phy_ctrl = 0;
++      int ret;
++
++      ret = at803x_prepare_config_aneg(phydev);
++      if (ret)
++              return ret;
++
++      /* The reg MII_BMCR also needs to be configured for force mode, the
++       * genphy_config_aneg is also needed.
++       */
++      if (phydev->autoneg == AUTONEG_DISABLE)
++              genphy_c45_pma_setup_forced(phydev);
++
++      if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
++              phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
++
++      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
++                                   MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
++      if (ret < 0)
++              return ret;
++
++      return __genphy_config_aneg(phydev, ret);
++}
++
+ static void qca808x_link_change_notify(struct phy_device *phydev)
+ {
+       /* Assert interface sgmii fifo on link down, deassert it on link up,
+@@ -2295,7 +2309,7 @@ static struct phy_driver at803x_driver[]
+       .set_wol                = at803x_set_wol,
+       .get_wol                = at803x_get_wol,
+       .get_features           = qca808x_get_features,
+-      .config_aneg            = at803x_config_aneg,
++      .config_aneg            = qca808x_config_aneg,
+       .suspend                = genphy_suspend,
+       .resume                 = genphy_resume,
+       .read_status            = qca808x_read_status,
diff --git a/target/linux/generic/backport-6.6/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch b/target/linux/generic/backport-6.6/708-v6.8-02-net-phy-at803x-make-read-specific-status-function-mo.patch
new file mode 100644 (file)
index 0000000..54ca3bd
--- /dev/null
@@ -0,0 +1,97 @@
+From 38eb804e8458ba181a03a0498ce4bf84eebd1931 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 14 Dec 2023 01:44:32 +0100
+Subject: [PATCH 2/2] net: phy: at803x: make read specific status function more
+ generic
+
+Rework read specific status function to be more generic. The function
+apply different speed mask based on the PHY ID. Make it more generic by
+adding an additional arg to pass the specific speed (ss) mask and use
+the provided mask to parse the speed value.
+
+This is needed to permit an easier deatch of qca808x code from the
+at803x driver.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 26 ++++++++++++++++++--------
+ 1 file changed, 18 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -301,6 +301,11 @@ static struct at803x_hw_stat qca83xx_hw_
+       { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
+ };
++struct at803x_ss_mask {
++      u16 speed_mask;
++      u8 speed_shift;
++};
++
+ struct at803x_priv {
+       int flags;
+       u16 clk_25m_reg;
+@@ -921,7 +926,8 @@ static void at803x_link_change_notify(st
+       }
+ }
+-static int at803x_read_specific_status(struct phy_device *phydev)
++static int at803x_read_specific_status(struct phy_device *phydev,
++                                     struct at803x_ss_mask ss_mask)
+ {
+       int ss;
+@@ -940,11 +946,8 @@ static int at803x_read_specific_status(s
+               if (sfc < 0)
+                       return sfc;
+-              /* qca8081 takes the different bits for speed value from at803x */
+-              if (phydev->drv->phy_id == QCA8081_PHY_ID)
+-                      speed = FIELD_GET(QCA808X_SS_SPEED_MASK, ss);
+-              else
+-                      speed = FIELD_GET(AT803X_SS_SPEED_MASK, ss);
++              speed = ss & ss_mask.speed_mask;
++              speed >>= ss_mask.speed_shift;
+               switch (speed) {
+               case AT803X_SS_SPEED_10:
+@@ -989,6 +992,7 @@ static int at803x_read_specific_status(s
+ static int at803x_read_status(struct phy_device *phydev)
+ {
+       struct at803x_priv *priv = phydev->priv;
++      struct at803x_ss_mask ss_mask = { 0 };
+       int err, old_link = phydev->link;
+       if (priv->is_1000basex)
+@@ -1012,7 +1016,9 @@ static int at803x_read_status(struct phy
+       if (err < 0)
+               return err;
+-      err = at803x_read_specific_status(phydev);
++      ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
++      ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
++      err = at803x_read_specific_status(phydev, ss_mask);
+       if (err < 0)
+               return err;
+@@ -1869,6 +1875,7 @@ static int qca808x_config_init(struct ph
+ static int qca808x_read_status(struct phy_device *phydev)
+ {
++      struct at803x_ss_mask ss_mask = { 0 };
+       int ret;
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+@@ -1882,7 +1889,10 @@ static int qca808x_read_status(struct ph
+       if (ret)
+               return ret;
+-      ret = at803x_read_specific_status(phydev);
++      /* qca8081 takes the different bits for speed value from at803x */
++      ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
++      ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
++      ret = at803x_read_specific_status(phydev, ss_mask);
+       if (ret < 0)
+               return ret;
diff --git a/target/linux/generic/backport-6.6/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch b/target/linux/generic/backport-6.6/709-v6.8-01-net-phy-at803x-remove-extra-space-after-cast.patch
new file mode 100644 (file)
index 0000000..8097a33
--- /dev/null
@@ -0,0 +1,27 @@
+From fc9d7264ddc32eaa647d6bfcdc25cdf9f786fde0 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 18 Dec 2023 00:27:39 +0100
+Subject: [PATCH 1/2] net: phy: at803x: remove extra space after cast
+
+Remove extra space after cast as reported by checkpatch to keep code
+clean.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20231217232739.27065-1-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/at803x.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -462,7 +462,7 @@ static int at803x_set_wol(struct phy_dev
+               if (!ndev)
+                       return -ENODEV;
+-              mac = (const u8 *) ndev->dev_addr;
++              mac = (const u8 *)ndev->dev_addr;
+               if (!is_valid_ether_addr(mac))
+                       return -EINVAL;
diff --git a/target/linux/generic/backport-6.6/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch b/target/linux/generic/backport-6.6/709-v6.8-02-net-phy-at803x-replace-msleep-1-with-usleep_range.patch
new file mode 100644 (file)
index 0000000..b8d4591
--- /dev/null
@@ -0,0 +1,38 @@
+From 3ab5720881a924fb6405d9e6a3b09f1026467c47 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 18 Dec 2023 00:25:08 +0100
+Subject: [PATCH 2/2] net: phy: at803x: replace msleep(1) with usleep_range
+
+Replace msleep(1) with usleep_range as suggested by timers-howto guide.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20231217232508.26470-1-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/at803x.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -916,9 +916,9 @@ static void at803x_link_change_notify(st
+               at803x_context_save(phydev, &context);
+               phy_device_reset(phydev, 1);
+-              msleep(1);
++              usleep_range(1000, 2000);
+               phy_device_reset(phydev, 0);
+-              msleep(1);
++              usleep_range(1000, 2000);
+               at803x_context_restore(phydev, &context);
+@@ -1733,7 +1733,7 @@ static int qca83xx_resume(struct phy_dev
+       if (ret)
+               return ret;
+-      msleep(1);
++      usleep_range(1000, 2000);
+       return 0;
+ }
diff --git a/target/linux/generic/backport-6.6/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch b/target/linux/generic/backport-6.6/710-v6.8-net-phy-at803x-better-align-function-varibles-to-ope.patch
new file mode 100644 (file)
index 0000000..776b7bb
--- /dev/null
@@ -0,0 +1,152 @@
+From 7961ef1fa10ec35ad6923fb5751877116e4b035b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 19 Dec 2023 21:21:24 +0100
+Subject: [PATCH] net: phy: at803x: better align function varibles to open
+ parenthesis
+
+Better align function variables to open parenthesis as suggested by
+checkpatch script for qca808x function to make code cleaner.
+
+For cable_test_get_status function some additional rework was needed to
+handle too long functions.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 67 ++++++++++++++++++++++------------------
+ 1 file changed, 37 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1781,27 +1781,27 @@ static int qca808x_phy_fast_retrain_conf
+               return ret;
+       phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
+-                      QCA808X_TOP_OPTION1_DATA);
++                    QCA808X_TOP_OPTION1_DATA);
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
+-                      QCA808X_MSE_THRESHOLD_20DB_VALUE);
++                    QCA808X_MSE_THRESHOLD_20DB_VALUE);
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
+-                      QCA808X_MSE_THRESHOLD_17DB_VALUE);
++                    QCA808X_MSE_THRESHOLD_17DB_VALUE);
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
+-                      QCA808X_MSE_THRESHOLD_27DB_VALUE);
++                    QCA808X_MSE_THRESHOLD_27DB_VALUE);
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
+-                      QCA808X_MSE_THRESHOLD_28DB_VALUE);
++                    QCA808X_MSE_THRESHOLD_28DB_VALUE);
+       phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
+-                      QCA808X_MMD3_DEBUG_1_VALUE);
++                    QCA808X_MMD3_DEBUG_1_VALUE);
+       phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
+-                      QCA808X_MMD3_DEBUG_4_VALUE);
++                    QCA808X_MMD3_DEBUG_4_VALUE);
+       phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
+-                      QCA808X_MMD3_DEBUG_5_VALUE);
++                    QCA808X_MMD3_DEBUG_5_VALUE);
+       phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
+-                      QCA808X_MMD3_DEBUG_3_VALUE);
++                    QCA808X_MMD3_DEBUG_3_VALUE);
+       phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
+-                      QCA808X_MMD3_DEBUG_6_VALUE);
++                    QCA808X_MMD3_DEBUG_6_VALUE);
+       phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
+-                      QCA808X_MMD3_DEBUG_2_VALUE);
++                    QCA808X_MMD3_DEBUG_2_VALUE);
+       return 0;
+ }
+@@ -1838,13 +1838,14 @@ static int qca808x_config_init(struct ph
+       /* Active adc&vga on 802.3az for the link 1000M and 100M */
+       ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
+-                      QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
++                           QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
+       if (ret)
+               return ret;
+       /* Adjust the threshold on 802.3az for the link 1000M */
+       ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
+-                      QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL);
++                          QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
++                          QCA808X_MMD3_AZ_TRAINING_VAL);
+       if (ret)
+               return ret;
+@@ -1870,7 +1871,8 @@ static int qca808x_config_init(struct ph
+       /* Configure adc threshold as 100mv for the link 10M */
+       return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
+-                      QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
++                                   QCA808X_ADC_THRESHOLD_MASK,
++                                   QCA808X_ADC_THRESHOLD_100MV);
+ }
+ static int qca808x_read_status(struct phy_device *phydev)
+@@ -1883,7 +1885,7 @@ static int qca808x_read_status(struct ph
+               return ret;
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
+-                      ret & MDIO_AN_10GBT_STAT_LP2_5G);
++                       ret & MDIO_AN_10GBT_STAT_LP2_5G);
+       ret = genphy_read_status(phydev);
+       if (ret)
+@@ -1913,7 +1915,7 @@ static int qca808x_read_status(struct ph
+                */
+               if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
+                       if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
+-                                      qca808x_is_prefer_master(phydev)) {
++                          qca808x_is_prefer_master(phydev)) {
+                               qca808x_phy_ms_seed_enable(phydev, false);
+                       } else {
+                               qca808x_phy_ms_seed_enable(phydev, true);
+@@ -2070,18 +2072,22 @@ static int qca808x_cable_test_get_status
+       ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
+                               qca808x_cable_test_result_trans(pair_d));
+-      if (qca808x_cdt_fault_length_valid(pair_a))
+-              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,
+-                              qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A));
+-      if (qca808x_cdt_fault_length_valid(pair_b))
+-              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B,
+-                              qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B));
+-      if (qca808x_cdt_fault_length_valid(pair_c))
+-              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C,
+-                              qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C));
+-      if (qca808x_cdt_fault_length_valid(pair_d))
+-              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D,
+-                              qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D));
++      if (qca808x_cdt_fault_length_valid(pair_a)) {
++              val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A);
++              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
++      }
++      if (qca808x_cdt_fault_length_valid(pair_b)) {
++              val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B);
++              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
++      }
++      if (qca808x_cdt_fault_length_valid(pair_c)) {
++              val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C);
++              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
++      }
++      if (qca808x_cdt_fault_length_valid(pair_d)) {
++              val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D);
++              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
++      }
+       *finished = true;
+@@ -2148,8 +2154,9 @@ static void qca808x_link_change_notify(s
+        * the interface device address is always phy address added by 1.
+        */
+       mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
+-                      MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
+-                      QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
++                                 MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
++                                 QCA8081_PHY_FIFO_RSTN,
++                                 phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
+ }
+ static struct phy_driver at803x_driver[] = {
diff --git a/target/linux/generic/backport-6.6/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch b/target/linux/generic/backport-6.6/711-v6.8-01-net-phy-at803x-generalize-cdt-fault-length-function.patch
new file mode 100644 (file)
index 0000000..d7196da
--- /dev/null
@@ -0,0 +1,62 @@
+From 22eb276098da820d9440fad22901f9b74ed4d659 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 4 Jan 2024 22:30:38 +0100
+Subject: [PATCH 1/4] net: phy: at803x: generalize cdt fault length function
+
+Generalize cable test fault length function since they all base on the
+same magic values (already reverse engineered to understand the meaning
+of it) to have consistenct values on every PHY.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1192,10 +1192,8 @@ static bool at803x_cdt_fault_length_vali
+       return false;
+ }
+-static int at803x_cdt_fault_length(u16 status)
++static int at803x_cdt_fault_length(int dt)
+ {
+-      int dt;
+-
+       /* According to the datasheet the distance to the fault is
+        * DELTA_TIME * 0.824 meters.
+        *
+@@ -1211,8 +1209,6 @@ static int at803x_cdt_fault_length(u16 s
+        * With a VF of 0.69 we get the factor 0.824 mentioned in the
+        * datasheet.
+        */
+-      dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status);
+-
+       return (dt * 824) / 10;
+ }
+@@ -1265,9 +1261,11 @@ static int at803x_cable_test_one_pair(st
+       ethnl_cable_test_result(phydev, ethtool_pair[pair],
+                               at803x_cable_test_result_trans(val));
+-      if (at803x_cdt_fault_length_valid(val))
++      if (at803x_cdt_fault_length_valid(val)) {
++              val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val);
+               ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
+                                             at803x_cdt_fault_length(val));
++      }
+       return 1;
+ }
+@@ -1992,7 +1990,8 @@ static int qca808x_cdt_fault_length(stru
+       if (val < 0)
+               return val;
+-      return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10;
++      val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val);
++      return at803x_cdt_fault_length(val);
+ }
+ static int qca808x_cable_test_start(struct phy_device *phydev)
diff --git a/target/linux/generic/backport-6.6/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch b/target/linux/generic/backport-6.6/711-v6.8-02-net-phy-at803x-refactor-qca808x-cable-test-get-statu.patch
new file mode 100644 (file)
index 0000000..5d38fe7
--- /dev/null
@@ -0,0 +1,118 @@
+From e0e9ada1df6133513249861c1d91c1dbefd9383b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 4 Jan 2024 22:30:39 +0100
+Subject: [PATCH 2/4] net: phy: at803x: refactor qca808x cable test get status
+ function
+
+Refactor qca808x cable test get status function to remove code
+duplication and clean things up.
+
+The same logic is applied to each pair hence it can be generalized and
+moved to a common function.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 80 ++++++++++++++++++++++++----------------
+ 1 file changed, 49 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -2035,10 +2035,43 @@ static int qca808x_cable_test_start(stru
+       return 0;
+ }
++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
++                                            u16 status)
++{
++      u16 pair_code;
++      int length;
++
++      switch (pair) {
++      case ETHTOOL_A_CABLE_PAIR_A:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_B:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_C:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_D:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      ethnl_cable_test_result(phydev, pair,
++                              qca808x_cable_test_result_trans(pair_code));
++
++      if (qca808x_cdt_fault_length_valid(pair_code)) {
++              length = qca808x_cdt_fault_length(phydev, pair);
++              ethnl_cable_test_fault_length(phydev, pair, length);
++      }
++
++      return 0;
++}
++
+ static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
+ {
+       int ret, val;
+-      int pair_a, pair_b, pair_c, pair_d;
+       *finished = false;
+@@ -2057,36 +2090,21 @@ static int qca808x_cable_test_get_status
+       if (val < 0)
+               return val;
+-      pair_a = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, val);
+-      pair_b = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, val);
+-      pair_c = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, val);
+-      pair_d = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, val);
+-
+-      ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
+-                              qca808x_cable_test_result_trans(pair_a));
+-      ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
+-                              qca808x_cable_test_result_trans(pair_b));
+-      ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
+-                              qca808x_cable_test_result_trans(pair_c));
+-      ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
+-                              qca808x_cable_test_result_trans(pair_d));
+-
+-      if (qca808x_cdt_fault_length_valid(pair_a)) {
+-              val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A);
+-              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
+-      }
+-      if (qca808x_cdt_fault_length_valid(pair_b)) {
+-              val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B);
+-              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
+-      }
+-      if (qca808x_cdt_fault_length_valid(pair_c)) {
+-              val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C);
+-              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
+-      }
+-      if (qca808x_cdt_fault_length_valid(pair_d)) {
+-              val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D);
+-              ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
+-      }
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
++      if (ret)
++              return ret;
+       *finished = true;
diff --git a/target/linux/generic/backport-6.6/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch b/target/linux/generic/backport-6.6/711-v6.8-03-net-phy-at803x-add-support-for-cdt-cross-short-test-.patch
new file mode 100644 (file)
index 0000000..d579386
--- /dev/null
@@ -0,0 +1,182 @@
+From ea73e5ea442ee2aade67b1fb1233ccb3cbea2ceb Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 4 Jan 2024 22:30:40 +0100
+Subject: [PATCH 3/4] net: phy: at803x: add support for cdt cross short test
+ for qca808x
+
+QCA808x PHY Family supports Cable Diagnostic Test also for Cross Pair
+Short.
+
+Add all the define to make enable and support these additional tests.
+
+Cross Short test was previously disabled by default, this is now changed
+and enabled by default. In this mode, the mask changed a bit and length
+is shifted based on the fault condition.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 86 ++++++++++++++++++++++++++++++++--------
+ 1 file changed, 69 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -254,6 +254,7 @@
+ #define QCA808X_CDT_ENABLE_TEST                       BIT(15)
+ #define QCA808X_CDT_INTER_CHECK_DIS           BIT(13)
++#define QCA808X_CDT_STATUS                    BIT(11)
+ #define QCA808X_CDT_LENGTH_UNIT                       BIT(10)
+ #define QCA808X_MMD3_CDT_STATUS                       0x8064
+@@ -261,16 +262,44 @@
+ #define QCA808X_MMD3_CDT_DIAG_PAIR_B          0x8066
+ #define QCA808X_MMD3_CDT_DIAG_PAIR_C          0x8067
+ #define QCA808X_MMD3_CDT_DIAG_PAIR_D          0x8068
+-#define QCA808X_CDT_DIAG_LENGTH                       GENMASK(7, 0)
++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT    GENMASK(15, 8)
++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT   GENMASK(7, 0)
+ #define QCA808X_CDT_CODE_PAIR_A                       GENMASK(15, 12)
+ #define QCA808X_CDT_CODE_PAIR_B                       GENMASK(11, 8)
+ #define QCA808X_CDT_CODE_PAIR_C                       GENMASK(7, 4)
+ #define QCA808X_CDT_CODE_PAIR_D                       GENMASK(3, 0)
+-#define QCA808X_CDT_STATUS_STAT_FAIL          0
+-#define QCA808X_CDT_STATUS_STAT_NORMAL                1
+-#define QCA808X_CDT_STATUS_STAT_OPEN          2
+-#define QCA808X_CDT_STATUS_STAT_SHORT         3
++
++#define QCA808X_CDT_STATUS_STAT_TYPE          GENMASK(1, 0)
++#define QCA808X_CDT_STATUS_STAT_FAIL          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
++#define QCA808X_CDT_STATUS_STAT_NORMAL                FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN     FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT    FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
++
++#define QCA808X_CDT_STATUS_STAT_MDI           GENMASK(3, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI1          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
++#define QCA808X_CDT_STATUS_STAT_MDI2          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI3          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
++
++/* NORMAL are MDI with type set to 0 */
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI1
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI2
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI3
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
++
++/* Added for reference of existence but should be handled by wait_for_completion already */
++#define QCA808X_CDT_STATUS_STAT_BUSY          (BIT(1) | BIT(3))
+ /* QCA808X 1G chip type */
+ #define QCA808X_PHY_MMD7_CHIP_TYPE            0x901d
+@@ -1941,8 +1970,17 @@ static int qca808x_soft_reset(struct phy
+ static bool qca808x_cdt_fault_length_valid(int cdt_code)
+ {
+       switch (cdt_code) {
+-      case QCA808X_CDT_STATUS_STAT_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_OPEN:
++      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+               return true;
+       default:
+               return false;
+@@ -1954,17 +1992,28 @@ static int qca808x_cable_test_result_tra
+       switch (cdt_code) {
+       case QCA808X_CDT_STATUS_STAT_NORMAL:
+               return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+-      case QCA808X_CDT_STATUS_STAT_SHORT:
++      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+               return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+-      case QCA808X_CDT_STATUS_STAT_OPEN:
++      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+               return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++              return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
+       case QCA808X_CDT_STATUS_STAT_FAIL:
+       default:
+               return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+       }
+ }
+-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair)
++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
++                                  int result)
+ {
+       int val;
+       u32 cdt_length_reg = 0;
+@@ -1990,7 +2039,11 @@ static int qca808x_cdt_fault_length(stru
+       if (val < 0)
+               return val;
+-      val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val);
++      if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
++              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
++      else
++              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
++
+       return at803x_cdt_fault_length(val);
+ }
+@@ -2038,8 +2091,8 @@ static int qca808x_cable_test_start(stru
+ static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
+                                             u16 status)
+ {
++      int length, result;
+       u16 pair_code;
+-      int length;
+       switch (pair) {
+       case ETHTOOL_A_CABLE_PAIR_A:
+@@ -2058,11 +2111,11 @@ static int qca808x_cable_test_get_pair_s
+               return -EINVAL;
+       }
+-      ethnl_cable_test_result(phydev, pair,
+-                              qca808x_cable_test_result_trans(pair_code));
++      result = qca808x_cable_test_result_trans(pair_code);
++      ethnl_cable_test_result(phydev, pair, result);
+       if (qca808x_cdt_fault_length_valid(pair_code)) {
+-              length = qca808x_cdt_fault_length(phydev, pair);
++              length = qca808x_cdt_fault_length(phydev, pair, result);
+               ethnl_cable_test_fault_length(phydev, pair, length);
+       }
+@@ -2076,8 +2129,7 @@ static int qca808x_cable_test_get_status
+       *finished = false;
+       val = QCA808X_CDT_ENABLE_TEST |
+-            QCA808X_CDT_LENGTH_UNIT |
+-            QCA808X_CDT_INTER_CHECK_DIS;
++            QCA808X_CDT_LENGTH_UNIT;
+       ret = at803x_cdt_start(phydev, val);
+       if (ret)
+               return ret;
diff --git a/target/linux/generic/backport-6.6/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch b/target/linux/generic/backport-6.6/711-v6.8-04-net-phy-at803x-make-read_status-more-generic.patch
new file mode 100644 (file)
index 0000000..c146e50
--- /dev/null
@@ -0,0 +1,62 @@
+From c34d9452d4e5d98a655d7b625e85466320885416 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 4 Jan 2024 22:30:41 +0100
+Subject: [PATCH 4/4] net: phy: at803x: make read_status more generic
+
+Make read_status more generic in preparation on moving it to shared
+library as other PHY Family Driver will have the exact same
+implementation.
+
+The only specific part was a check for AR8031/33 if 1000basex was used.
+The check is moved to a dedicated function specific for those PHYs.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 17 ++++++++++++-----
+ 1 file changed, 12 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -1020,13 +1020,9 @@ static int at803x_read_specific_status(s
+ static int at803x_read_status(struct phy_device *phydev)
+ {
+-      struct at803x_priv *priv = phydev->priv;
+       struct at803x_ss_mask ss_mask = { 0 };
+       int err, old_link = phydev->link;
+-      if (priv->is_1000basex)
+-              return genphy_c37_read_status(phydev);
+-
+       /* Update the link, but return if there was an error */
+       err = genphy_update_link(phydev);
+       if (err)
+@@ -1618,6 +1614,17 @@ static int at8031_config_intr(struct phy
+       return at803x_config_intr(phydev);
+ }
++/* AR8031 and AR8033 share the same read status logic */
++static int at8031_read_status(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++
++      if (priv->is_1000basex)
++              return genphy_c37_read_status(phydev);
++
++      return at803x_read_status(phydev);
++}
++
+ /* AR8031 and AR8035 share the same cable test get status reg */
+ static int at8031_cable_test_get_status(struct phy_device *phydev,
+                                       bool *finished)
+@@ -2281,7 +2288,7 @@ static struct phy_driver at803x_driver[]
+       .read_page              = at803x_read_page,
+       .write_page             = at803x_write_page,
+       .get_features           = at803x_get_features,
+-      .read_status            = at803x_read_status,
++      .read_status            = at8031_read_status,
+       .config_intr            = at8031_config_intr,
+       .handle_interrupt       = at803x_handle_interrupt,
+       .get_tunable            = at803x_get_tunable,
diff --git a/target/linux/generic/backport-6.6/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch b/target/linux/generic/backport-6.6/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch
new file mode 100644 (file)
index 0000000..36675e7
--- /dev/null
@@ -0,0 +1,408 @@
+From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 25 Jan 2024 21:37:01 +0100
+Subject: [PATCH 5/5] net: phy: at803x: add LED support for qca808x
+
+Add LED support for QCA8081 PHY.
+
+Documentation for this LEDs PHY is very scarce even with NDA access
+to Documentation for OEMs. Only the blink pattern are documented and are
+very confusing most of the time. No documentation is present about
+forcing the LED on/off or to always blink.
+
+Those settings were reversed by poking the regs and trying to find the
+correct bits to trigger these modes. Some bits mode are not clear and
+maybe the documentation option are not 100% correct. For the sake of LED
+support the reversed option are enough to add support for current LED
+APIs.
+
+Supported HW control modes are:
+- tx
+- rx
+- link_10
+- link_100
+- link_1000
+- link_2500
+- half_duplex
+- full_duplex
+
+Also add support for LED polarity set to set LED polarity to active
+high or low. QSDK sets this value to high by default but PHY reset value
+doesn't have this enabled by default.
+
+QSDK also sets 2 additional bits but their usage is not clear, info about
+this is added in the header. It was verified that for correct function
+of the LED if active high is needed, only BIT 6 is needed.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240125203702.4552-6-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/at803x.c | 327 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 327 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -301,6 +301,87 @@
+ /* Added for reference of existence but should be handled by wait_for_completion already */
+ #define QCA808X_CDT_STATUS_STAT_BUSY          (BIT(1) | BIT(3))
++#define QCA808X_MMD7_LED_GLOBAL                       0x8073
++#define QCA808X_LED_BLINK_1                   GENMASK(11, 6)
++#define QCA808X_LED_BLINK_2                   GENMASK(5, 0)
++/* Values are the same for both BLINK_1 and BLINK_2 */
++#define QCA808X_LED_BLINK_FREQ_MASK           GENMASK(5, 3)
++#define QCA808X_LED_BLINK_FREQ_2HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
++#define QCA808X_LED_BLINK_FREQ_4HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
++#define QCA808X_LED_BLINK_FREQ_8HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
++#define QCA808X_LED_BLINK_FREQ_16HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
++#define QCA808X_LED_BLINK_FREQ_32HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
++#define QCA808X_LED_BLINK_FREQ_64HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
++#define QCA808X_LED_BLINK_FREQ_128HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
++#define QCA808X_LED_BLINK_FREQ_256HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
++#define QCA808X_LED_BLINK_DUTY_MASK           GENMASK(2, 0)
++#define QCA808X_LED_BLINK_DUTY_50_50          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
++#define QCA808X_LED_BLINK_DUTY_75_25          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
++#define QCA808X_LED_BLINK_DUTY_25_75          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
++#define QCA808X_LED_BLINK_DUTY_33_67          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
++#define QCA808X_LED_BLINK_DUTY_67_33          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
++#define QCA808X_LED_BLINK_DUTY_17_83          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
++#define QCA808X_LED_BLINK_DUTY_83_17          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
++#define QCA808X_LED_BLINK_DUTY_8_92           FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
++
++#define QCA808X_MMD7_LED2_CTRL                        0x8074
++#define QCA808X_MMD7_LED2_FORCE_CTRL          0x8075
++#define QCA808X_MMD7_LED1_CTRL                        0x8076
++#define QCA808X_MMD7_LED1_FORCE_CTRL          0x8077
++#define QCA808X_MMD7_LED0_CTRL                        0x8078
++#define QCA808X_MMD7_LED_CTRL(x)              (0x8078 - ((x) * 2))
++
++/* LED hw control pattern is the same for every LED */
++#define QCA808X_LED_PATTERN_MASK              GENMASK(15, 0)
++#define QCA808X_LED_SPEED2500_ON              BIT(15)
++#define QCA808X_LED_SPEED2500_BLINK           BIT(14)
++/* Follow blink trigger even if duplex or speed condition doesn't match */
++#define QCA808X_LED_BLINK_CHECK_BYPASS                BIT(13)
++#define QCA808X_LED_FULL_DUPLEX_ON            BIT(12)
++#define QCA808X_LED_HALF_DUPLEX_ON            BIT(11)
++#define QCA808X_LED_TX_BLINK                  BIT(10)
++#define QCA808X_LED_RX_BLINK                  BIT(9)
++#define QCA808X_LED_TX_ON_10MS                        BIT(8)
++#define QCA808X_LED_RX_ON_10MS                        BIT(7)
++#define QCA808X_LED_SPEED1000_ON              BIT(6)
++#define QCA808X_LED_SPEED100_ON                       BIT(5)
++#define QCA808X_LED_SPEED10_ON                        BIT(4)
++#define QCA808X_LED_COLLISION_BLINK           BIT(3)
++#define QCA808X_LED_SPEED1000_BLINK           BIT(2)
++#define QCA808X_LED_SPEED100_BLINK            BIT(1)
++#define QCA808X_LED_SPEED10_BLINK             BIT(0)
++
++#define QCA808X_MMD7_LED0_FORCE_CTRL          0x8079
++#define QCA808X_MMD7_LED_FORCE_CTRL(x)                (0x8079 - ((x) * 2))
++
++/* LED force ctrl is the same for every LED
++ * No documentation exist for this, not even internal one
++ * with NDA as QCOM gives only info about configuring
++ * hw control pattern rules and doesn't indicate any way
++ * to force the LED to specific mode.
++ * These define comes from reverse and testing and maybe
++ * lack of some info or some info are not entirely correct.
++ * For the basic LED control and hw control these finding
++ * are enough to support LED control in all the required APIs.
++ *
++ * On doing some comparison with implementation with qca807x,
++ * it was found that it's 1:1 equal to it and confirms all the
++ * reverse done. It was also found further specification with the
++ * force mode and the blink modes.
++ */
++#define QCA808X_LED_FORCE_EN                  BIT(15)
++#define QCA808X_LED_FORCE_MODE_MASK           GENMASK(14, 13)
++#define QCA808X_LED_FORCE_BLINK_1             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
++#define QCA808X_LED_FORCE_BLINK_2             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
++#define QCA808X_LED_FORCE_ON                  FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
++#define QCA808X_LED_FORCE_OFF                 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
++
++#define QCA808X_MMD7_LED_POLARITY_CTRL                0x901a
++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
++ * LED to active high. It's not clear what BIT 3 and BIT 4 does.
++ */
++#define QCA808X_LED_ACTIVE_HIGH                       BIT(6)
++
+ /* QCA808X 1G chip type */
+ #define QCA808X_PHY_MMD7_CHIP_TYPE            0x901d
+ #define QCA808X_PHY_CHIP_TYPE_1G              BIT(0)
+@@ -346,6 +427,7 @@ struct at803x_priv {
+       struct regulator_dev *vddio_rdev;
+       struct regulator_dev *vddh_rdev;
+       u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
++      int led_polarity_mode;
+ };
+ struct at803x_context {
+@@ -706,6 +788,9 @@ static int at803x_probe(struct phy_devic
+       if (!priv)
+               return -ENOMEM;
++      /* Init LED polarity mode to -1 */
++      priv->led_polarity_mode = -1;
++
+       phydev->priv = priv;
+       ret = at803x_parse_dt(phydev);
+@@ -2235,6 +2320,242 @@ static void qca808x_link_change_notify(s
+                                  phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
+ }
++static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
++                                  u16 *offload_trigger)
++{
++      /* Parsing specific to netdev trigger */
++      if (test_bit(TRIGGER_NETDEV_TX, &rules))
++              *offload_trigger |= QCA808X_LED_TX_BLINK;
++      if (test_bit(TRIGGER_NETDEV_RX, &rules))
++              *offload_trigger |= QCA808X_LED_RX_BLINK;
++      if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED10_ON;
++      if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED100_ON;
++      if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED1000_ON;
++      if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED2500_ON;
++      if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
++              *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
++      if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
++              *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
++
++      if (rules && !*offload_trigger)
++              return -EOPNOTSUPP;
++
++      /* Enable BLINK_CHECK_BYPASS by default to make the LED
++       * blink even with duplex or speed mode not enabled.
++       */
++      *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
++
++      return 0;
++}
++
++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++                                QCA808X_LED_FORCE_EN);
++}
++
++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                     unsigned long rules)
++{
++      u16 offload_trigger = 0;
++
++      if (index > 2)
++              return -EINVAL;
++
++      return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
++}
++
++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                    unsigned long rules)
++{
++      u16 reg, offload_trigger = 0;
++      int ret;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_CTRL(index);
++
++      ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
++      if (ret)
++              return ret;
++
++      ret = qca808x_led_hw_control_enable(phydev, index);
++      if (ret)
++              return ret;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                            QCA808X_LED_PATTERN_MASK,
++                            offload_trigger);
++}
++
++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++      int val;
++
++      if (index > 2)
++              return false;
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++
++      return !(val & QCA808X_LED_FORCE_EN);
++}
++
++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                    unsigned long *rules)
++{
++      u16 reg;
++      int val;
++
++      if (index > 2)
++              return -EINVAL;
++
++      /* Check if we have hw control enabled */
++      if (qca808x_led_hw_control_status(phydev, index))
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_CTRL(index);
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++      if (val & QCA808X_LED_TX_BLINK)
++              set_bit(TRIGGER_NETDEV_TX, rules);
++      if (val & QCA808X_LED_RX_BLINK)
++              set_bit(TRIGGER_NETDEV_RX, rules);
++      if (val & QCA808X_LED_SPEED10_ON)
++              set_bit(TRIGGER_NETDEV_LINK_10, rules);
++      if (val & QCA808X_LED_SPEED100_ON)
++              set_bit(TRIGGER_NETDEV_LINK_100, rules);
++      if (val & QCA808X_LED_SPEED1000_ON)
++              set_bit(TRIGGER_NETDEV_LINK_1000, rules);
++      if (val & QCA808X_LED_SPEED2500_ON)
++              set_bit(TRIGGER_NETDEV_LINK_2500, rules);
++      if (val & QCA808X_LED_HALF_DUPLEX_ON)
++              set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
++      if (val & QCA808X_LED_FULL_DUPLEX_ON)
++              set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
++
++      return 0;
++}
++
++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_CTRL(index);
++
++      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++                                QCA808X_LED_PATTERN_MASK);
++}
++
++static int qca808x_led_brightness_set(struct phy_device *phydev,
++                                    u8 index, enum led_brightness value)
++{
++      u16 reg;
++      int ret;
++
++      if (index > 2)
++              return -EINVAL;
++
++      if (!value) {
++              ret = qca808x_led_hw_control_reset(phydev, index);
++              if (ret)
++                      return ret;
++      }
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                            QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++                            QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
++                                                           QCA808X_LED_FORCE_OFF);
++}
++
++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
++                               unsigned long *delay_on,
++                               unsigned long *delay_off)
++{
++      int ret;
++      u16 reg;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      /* Set blink to 50% off, 50% on at 4Hz by default */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
++                           QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
++                           QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
++      if (ret)
++              return ret;
++
++      /* We use BLINK_1 for normal blinking */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
++      if (ret)
++              return ret;
++
++      /* We set blink to 4Hz, aka 250ms */
++      *delay_on = 250 / 2;
++      *delay_off = 250 / 2;
++
++      return 0;
++}
++
++static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
++                                  unsigned long modes)
++{
++      struct at803x_priv *priv = phydev->priv;
++      bool active_low = false;
++      u32 mode;
++
++      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++              switch (mode) {
++              case PHY_LED_ACTIVE_LOW:
++                      active_low = true;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      /* PHY polarity is global and can't be set per LED.
++       * To detect this, check if last requested polarity mode
++       * match the new one.
++       */
++      if (priv->led_polarity_mode >= 0 &&
++          priv->led_polarity_mode != active_low) {
++              phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
++              return -EINVAL;
++      }
++
++      /* Save the last PHY polarity mode */
++      priv->led_polarity_mode = active_low;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN,
++                            QCA808X_MMD7_LED_POLARITY_CTRL,
++                            QCA808X_LED_ACTIVE_HIGH,
++                            active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+       /* Qualcomm Atheros AR8035 */
+@@ -2411,6 +2732,12 @@ static struct phy_driver at803x_driver[]
+       .cable_test_start       = qca808x_cable_test_start,
+       .cable_test_get_status  = qca808x_cable_test_get_status,
+       .link_change_notify     = qca808x_link_change_notify,
++      .led_brightness_set     = qca808x_led_brightness_set,
++      .led_blink_set          = qca808x_led_blink_set,
++      .led_hw_is_supported    = qca808x_led_hw_is_supported,
++      .led_hw_control_set     = qca808x_led_hw_control_set,
++      .led_hw_control_get     = qca808x_led_hw_control_get,
++      .led_polarity_set       = qca808x_led_polarity_set,
+ }, };
+ module_phy_driver(at803x_driver);
diff --git a/target/linux/generic/backport-6.6/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch b/target/linux/generic/backport-6.6/713-v6.9-01-net-phy-move-at803x-PHY-driver-to-dedicated-director.patch
new file mode 100644 (file)
index 0000000..ba89ecd
--- /dev/null
@@ -0,0 +1,5598 @@
+From 9e56ff53b4115875667760445b028357848b4748 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 29 Jan 2024 15:15:19 +0100
+Subject: [PATCH 1/5] net: phy: move at803x PHY driver to dedicated directory
+
+In preparation for addition of other Qcom PHY and to tidy things up,
+move the at803x PHY driver to dedicated directory.
+
+The same order in the Kconfig selection is saved.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240129141600.2592-2-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/Kconfig             | 7 +------
+ drivers/net/phy/Makefile            | 2 +-
+ drivers/net/phy/qcom/Kconfig        | 7 +++++++
+ drivers/net/phy/qcom/Makefile       | 2 ++
+ drivers/net/phy/{ => qcom}/at803x.c | 0
+ 5 files changed, 11 insertions(+), 7 deletions(-)
+ create mode 100644 drivers/net/phy/qcom/Kconfig
+ create mode 100644 drivers/net/phy/qcom/Makefile
+ rename drivers/net/phy/{ => qcom}/at803x.c (100%)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -318,12 +318,7 @@ config NCN26000_PHY
+         Currently supports the NCN26000 10BASE-T1S Industrial PHY
+         with MII interface.
+-config AT803X_PHY
+-      tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
+-      depends on REGULATOR
+-      help
+-        Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
+-        QCA8337(Internal qca8k PHY) model
++source "drivers/net/phy/qcom/Kconfig"
+ config QSEMI_PHY
+       tristate "Quality Semiconductor PHYs"
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -36,7 +36,6 @@ obj-$(CONFIG_ADIN_PHY)               += adin.o
+ obj-$(CONFIG_ADIN1100_PHY)    += adin1100.o
+ obj-$(CONFIG_AMD_PHY)         += amd.o
+ obj-$(CONFIG_AQUANTIA_PHY)    += aquantia/
+-obj-$(CONFIG_AT803X_PHY)      += at803x.o
+ obj-$(CONFIG_AX88796B_PHY)    += ax88796b.o
+ obj-$(CONFIG_BCM54140_PHY)    += bcm54140.o
+ obj-$(CONFIG_BCM63XX_PHY)     += bcm63xx.o
+@@ -82,6 +81,7 @@ obj-$(CONFIG_NCN26000_PHY)   += ncn26000.o
+ obj-$(CONFIG_NXP_C45_TJA11XX_PHY)     += nxp-c45-tja11xx.o
+ obj-$(CONFIG_NXP_CBTX_PHY)    += nxp-cbtx.o
+ obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
++obj-y                         += qcom/
+ obj-$(CONFIG_QSEMI_PHY)               += qsemi.o
+ obj-$(CONFIG_REALTEK_PHY)     += realtek.o
+ obj-$(CONFIG_RENESAS_PHY)     += uPD60620.o
+--- /dev/null
++++ b/drivers/net/phy/qcom/Kconfig
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0-only
++config AT803X_PHY
++      tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
++      depends on REGULATOR
++      help
++        Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
++        QCA8337(Internal qca8k PHY) model
+--- /dev/null
++++ b/drivers/net/phy/qcom/Makefile
+@@ -0,0 +1,2 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_AT803X_PHY)      += at803x.o
+--- a/drivers/net/phy/at803x.c
++++ /dev/null
+@@ -1,2759 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0+
+-/*
+- * drivers/net/phy/at803x.c
+- *
+- * Driver for Qualcomm Atheros AR803x PHY
+- *
+- * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
+- */
+-
+-#include <linux/phy.h>
+-#include <linux/module.h>
+-#include <linux/string.h>
+-#include <linux/netdevice.h>
+-#include <linux/etherdevice.h>
+-#include <linux/ethtool_netlink.h>
+-#include <linux/bitfield.h>
+-#include <linux/regulator/of_regulator.h>
+-#include <linux/regulator/driver.h>
+-#include <linux/regulator/consumer.h>
+-#include <linux/of.h>
+-#include <linux/phylink.h>
+-#include <linux/sfp.h>
+-#include <dt-bindings/net/qca-ar803x.h>
+-
+-#define AT803X_SPECIFIC_FUNCTION_CONTROL      0x10
+-#define AT803X_SFC_ASSERT_CRS                 BIT(11)
+-#define AT803X_SFC_FORCE_LINK                 BIT(10)
+-#define AT803X_SFC_MDI_CROSSOVER_MODE_M               GENMASK(6, 5)
+-#define AT803X_SFC_AUTOMATIC_CROSSOVER                0x3
+-#define AT803X_SFC_MANUAL_MDIX                        0x1
+-#define AT803X_SFC_MANUAL_MDI                 0x0
+-#define AT803X_SFC_SQE_TEST                   BIT(2)
+-#define AT803X_SFC_POLARITY_REVERSAL          BIT(1)
+-#define AT803X_SFC_DISABLE_JABBER             BIT(0)
+-
+-#define AT803X_SPECIFIC_STATUS                        0x11
+-#define AT803X_SS_SPEED_MASK                  GENMASK(15, 14)
+-#define AT803X_SS_SPEED_1000                  2
+-#define AT803X_SS_SPEED_100                   1
+-#define AT803X_SS_SPEED_10                    0
+-#define AT803X_SS_DUPLEX                      BIT(13)
+-#define AT803X_SS_SPEED_DUPLEX_RESOLVED               BIT(11)
+-#define AT803X_SS_MDIX                                BIT(6)
+-
+-#define QCA808X_SS_SPEED_MASK                 GENMASK(9, 7)
+-#define QCA808X_SS_SPEED_2500                 4
+-
+-#define AT803X_INTR_ENABLE                    0x12
+-#define AT803X_INTR_ENABLE_AUTONEG_ERR                BIT(15)
+-#define AT803X_INTR_ENABLE_SPEED_CHANGED      BIT(14)
+-#define AT803X_INTR_ENABLE_DUPLEX_CHANGED     BIT(13)
+-#define AT803X_INTR_ENABLE_PAGE_RECEIVED      BIT(12)
+-#define AT803X_INTR_ENABLE_LINK_FAIL          BIT(11)
+-#define AT803X_INTR_ENABLE_LINK_SUCCESS               BIT(10)
+-#define AT803X_INTR_ENABLE_LINK_FAIL_BX               BIT(8)
+-#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX    BIT(7)
+-#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE        BIT(5)
+-#define AT803X_INTR_ENABLE_POLARITY_CHANGED   BIT(1)
+-#define AT803X_INTR_ENABLE_WOL                        BIT(0)
+-
+-#define AT803X_INTR_STATUS                    0x13
+-
+-#define AT803X_SMART_SPEED                    0x14
+-#define AT803X_SMART_SPEED_ENABLE             BIT(5)
+-#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK   GENMASK(4, 2)
+-#define AT803X_SMART_SPEED_BYPASS_TIMER               BIT(1)
+-#define AT803X_CDT                            0x16
+-#define AT803X_CDT_MDI_PAIR_MASK              GENMASK(9, 8)
+-#define AT803X_CDT_ENABLE_TEST                        BIT(0)
+-#define AT803X_CDT_STATUS                     0x1c
+-#define AT803X_CDT_STATUS_STAT_NORMAL         0
+-#define AT803X_CDT_STATUS_STAT_SHORT          1
+-#define AT803X_CDT_STATUS_STAT_OPEN           2
+-#define AT803X_CDT_STATUS_STAT_FAIL           3
+-#define AT803X_CDT_STATUS_STAT_MASK           GENMASK(9, 8)
+-#define AT803X_CDT_STATUS_DELTA_TIME_MASK     GENMASK(7, 0)
+-#define AT803X_LED_CONTROL                    0x18
+-
+-#define AT803X_PHY_MMD3_WOL_CTRL              0x8012
+-#define AT803X_WOL_EN                         BIT(5)
+-#define AT803X_LOC_MAC_ADDR_0_15_OFFSET               0x804C
+-#define AT803X_LOC_MAC_ADDR_16_31_OFFSET      0x804B
+-#define AT803X_LOC_MAC_ADDR_32_47_OFFSET      0x804A
+-#define AT803X_REG_CHIP_CONFIG                        0x1f
+-#define AT803X_BT_BX_REG_SEL                  0x8000
+-
+-#define AT803X_DEBUG_ADDR                     0x1D
+-#define AT803X_DEBUG_DATA                     0x1E
+-
+-#define AT803X_MODE_CFG_MASK                  0x0F
+-#define AT803X_MODE_CFG_BASET_RGMII           0x00
+-#define AT803X_MODE_CFG_BASET_SGMII           0x01
+-#define AT803X_MODE_CFG_BX1000_RGMII_50OHM    0x02
+-#define AT803X_MODE_CFG_BX1000_RGMII_75OHM    0x03
+-#define AT803X_MODE_CFG_BX1000_CONV_50OHM     0x04
+-#define AT803X_MODE_CFG_BX1000_CONV_75OHM     0x05
+-#define AT803X_MODE_CFG_FX100_RGMII_50OHM     0x06
+-#define AT803X_MODE_CFG_FX100_CONV_50OHM      0x07
+-#define AT803X_MODE_CFG_RGMII_AUTO_MDET               0x0B
+-#define AT803X_MODE_CFG_FX100_RGMII_75OHM     0x0E
+-#define AT803X_MODE_CFG_FX100_CONV_75OHM      0x0F
+-
+-#define AT803X_PSSR                           0x11    /*PHY-Specific Status Register*/
+-#define AT803X_PSSR_MR_AN_COMPLETE            0x0200
+-
+-#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_SYSTEM_CTRL_MODE         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_HIB_CTRL_PS_HIB_EN     BIT(15)
+-
+-#define AT803X_DEBUG_REG_3C                   0x3C
+-
+-#define AT803X_DEBUG_REG_GREEN                        0x3D
+-#define   AT803X_DEBUG_GATE_CLK_IN1000                BIT(6)
+-
+-#define AT803X_DEBUG_REG_1F                   0x1F
+-#define AT803X_DEBUG_PLL_ON                   BIT(2)
+-#define AT803X_DEBUG_RGMII_1V8                        BIT(3)
+-
+-#define MDIO_AZ_DEBUG                         0x800D
+-
+-/* AT803x supports either the XTAL input pad, an internal PLL or the
+- * DSP as clock reference for the clock output pad. The XTAL reference
+- * is only used for 25 MHz output, all other frequencies need the PLL.
+- * The DSP as a clock reference is used in synchronous ethernet
+- * applications.
+- *
+- * By default the PLL is only enabled if there is a link. Otherwise
+- * the PHY will go into low power state and disabled the PLL. You can
+- * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always
+- * enabled.
+- */
+-#define AT803X_MMD7_CLK25M                    0x8016
+-#define AT803X_CLK_OUT_MASK                   GENMASK(4, 2)
+-#define AT803X_CLK_OUT_25MHZ_XTAL             0
+-#define AT803X_CLK_OUT_25MHZ_DSP              1
+-#define AT803X_CLK_OUT_50MHZ_PLL              2
+-#define AT803X_CLK_OUT_50MHZ_DSP              3
+-#define AT803X_CLK_OUT_62_5MHZ_PLL            4
+-#define AT803X_CLK_OUT_62_5MHZ_DSP            5
+-#define AT803X_CLK_OUT_125MHZ_PLL             6
+-#define AT803X_CLK_OUT_125MHZ_DSP             7
+-
+-/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask
+- * but doesn't support choosing between XTAL/PLL and DSP.
+- */
+-#define AT8035_CLK_OUT_MASK                   GENMASK(4, 3)
+-
+-#define AT803X_CLK_OUT_STRENGTH_MASK          GENMASK(8, 7)
+-#define AT803X_CLK_OUT_STRENGTH_FULL          0
+-#define AT803X_CLK_OUT_STRENGTH_HALF          1
+-#define AT803X_CLK_OUT_STRENGTH_QUARTER               2
+-
+-#define AT803X_DEFAULT_DOWNSHIFT              5
+-#define AT803X_MIN_DOWNSHIFT                  2
+-#define AT803X_MAX_DOWNSHIFT                  9
+-
+-#define AT803X_MMD3_SMARTEEE_CTL1             0x805b
+-#define AT803X_MMD3_SMARTEEE_CTL2             0x805c
+-#define AT803X_MMD3_SMARTEEE_CTL3             0x805d
+-#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN      BIT(8)
+-
+-#define ATH9331_PHY_ID                                0x004dd041
+-#define ATH8030_PHY_ID                                0x004dd076
+-#define ATH8031_PHY_ID                                0x004dd074
+-#define ATH8032_PHY_ID                                0x004dd023
+-#define ATH8035_PHY_ID                                0x004dd072
+-#define AT8030_PHY_ID_MASK                    0xffffffef
+-
+-#define QCA8081_PHY_ID                                0x004dd101
+-
+-#define QCA8327_A_PHY_ID                      0x004dd033
+-#define QCA8327_B_PHY_ID                      0x004dd034
+-#define QCA8337_PHY_ID                                0x004dd036
+-#define QCA9561_PHY_ID                                0x004dd042
+-#define QCA8K_PHY_ID_MASK                     0xffffffff
+-
+-#define QCA8K_DEVFLAGS_REVISION_MASK          GENMASK(2, 0)
+-
+-#define AT803X_PAGE_FIBER                     0
+-#define AT803X_PAGE_COPPER                    1
+-
+-/* don't turn off internal PLL */
+-#define AT803X_KEEP_PLL_ENABLED                       BIT(0)
+-#define AT803X_DISABLE_SMARTEEE                       BIT(1)
+-
+-/* disable hibernation mode */
+-#define AT803X_DISABLE_HIBERNATION_MODE               BIT(2)
+-
+-/* ADC threshold */
+-#define QCA808X_PHY_DEBUG_ADC_THRESHOLD               0x2c80
+-#define QCA808X_ADC_THRESHOLD_MASK            GENMASK(7, 0)
+-#define QCA808X_ADC_THRESHOLD_80MV            0
+-#define QCA808X_ADC_THRESHOLD_100MV           0xf0
+-#define QCA808X_ADC_THRESHOLD_200MV           0x0f
+-#define QCA808X_ADC_THRESHOLD_300MV           0xff
+-
+-/* CLD control */
+-#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7               0x8007
+-#define QCA808X_8023AZ_AFE_CTRL_MASK          GENMASK(8, 4)
+-#define QCA808X_8023AZ_AFE_EN                 0x90
+-
+-/* AZ control */
+-#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL     0x8008
+-#define QCA808X_MMD3_AZ_TRAINING_VAL          0x1c32
+-
+-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB   0x8014
+-#define QCA808X_MSE_THRESHOLD_20DB_VALUE      0x529
+-
+-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB   0x800E
+-#define QCA808X_MSE_THRESHOLD_17DB_VALUE      0x341
+-
+-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB   0x801E
+-#define QCA808X_MSE_THRESHOLD_27DB_VALUE      0x419
+-
+-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB   0x8020
+-#define QCA808X_MSE_THRESHOLD_28DB_VALUE      0x341
+-
+-#define QCA808X_PHY_MMD7_TOP_OPTION1          0x901c
+-#define QCA808X_TOP_OPTION1_DATA              0x0
+-
+-#define QCA808X_PHY_MMD3_DEBUG_1              0xa100
+-#define QCA808X_MMD3_DEBUG_1_VALUE            0x9203
+-#define QCA808X_PHY_MMD3_DEBUG_2              0xa101
+-#define QCA808X_MMD3_DEBUG_2_VALUE            0x48ad
+-#define QCA808X_PHY_MMD3_DEBUG_3              0xa103
+-#define QCA808X_MMD3_DEBUG_3_VALUE            0x1698
+-#define QCA808X_PHY_MMD3_DEBUG_4              0xa105
+-#define QCA808X_MMD3_DEBUG_4_VALUE            0x8001
+-#define QCA808X_PHY_MMD3_DEBUG_5              0xa106
+-#define QCA808X_MMD3_DEBUG_5_VALUE            0x1111
+-#define QCA808X_PHY_MMD3_DEBUG_6              0xa011
+-#define QCA808X_MMD3_DEBUG_6_VALUE            0x5f85
+-
+-/* master/slave seed config */
+-#define QCA808X_PHY_DEBUG_LOCAL_SEED          9
+-#define QCA808X_MASTER_SLAVE_SEED_ENABLE      BIT(1)
+-#define QCA808X_MASTER_SLAVE_SEED_CFG         GENMASK(12, 2)
+-#define QCA808X_MASTER_SLAVE_SEED_RANGE               0x32
+-
+-/* Hibernation yields lower power consumpiton in contrast with normal operation mode.
+- * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.
+- */
+-#define QCA808X_DBG_AN_TEST                   0xb
+-#define QCA808X_HIBERNATION_EN                        BIT(15)
+-
+-#define QCA808X_CDT_ENABLE_TEST                       BIT(15)
+-#define QCA808X_CDT_INTER_CHECK_DIS           BIT(13)
+-#define QCA808X_CDT_STATUS                    BIT(11)
+-#define QCA808X_CDT_LENGTH_UNIT                       BIT(10)
+-
+-#define QCA808X_MMD3_CDT_STATUS                       0x8064
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_A          0x8065
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_B          0x8066
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_C          0x8067
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_D          0x8068
+-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT    GENMASK(15, 8)
+-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT   GENMASK(7, 0)
+-
+-#define QCA808X_CDT_CODE_PAIR_A                       GENMASK(15, 12)
+-#define QCA808X_CDT_CODE_PAIR_B                       GENMASK(11, 8)
+-#define QCA808X_CDT_CODE_PAIR_C                       GENMASK(7, 4)
+-#define QCA808X_CDT_CODE_PAIR_D                       GENMASK(3, 0)
+-
+-#define QCA808X_CDT_STATUS_STAT_TYPE          GENMASK(1, 0)
+-#define QCA808X_CDT_STATUS_STAT_FAIL          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
+-#define QCA808X_CDT_STATUS_STAT_NORMAL                FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
+-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN     FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
+-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT    FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
+-
+-#define QCA808X_CDT_STATUS_STAT_MDI           GENMASK(3, 2)
+-#define QCA808X_CDT_STATUS_STAT_MDI1          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
+-#define QCA808X_CDT_STATUS_STAT_MDI2          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
+-#define QCA808X_CDT_STATUS_STAT_MDI3          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
+-
+-/* NORMAL are MDI with type set to 0 */
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI1
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI2
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI3
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
+-
+-/* Added for reference of existence but should be handled by wait_for_completion already */
+-#define QCA808X_CDT_STATUS_STAT_BUSY          (BIT(1) | BIT(3))
+-
+-#define QCA808X_MMD7_LED_GLOBAL                       0x8073
+-#define QCA808X_LED_BLINK_1                   GENMASK(11, 6)
+-#define QCA808X_LED_BLINK_2                   GENMASK(5, 0)
+-/* Values are the same for both BLINK_1 and BLINK_2 */
+-#define QCA808X_LED_BLINK_FREQ_MASK           GENMASK(5, 3)
+-#define QCA808X_LED_BLINK_FREQ_2HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
+-#define QCA808X_LED_BLINK_FREQ_4HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
+-#define QCA808X_LED_BLINK_FREQ_8HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
+-#define QCA808X_LED_BLINK_FREQ_16HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
+-#define QCA808X_LED_BLINK_FREQ_32HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
+-#define QCA808X_LED_BLINK_FREQ_64HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
+-#define QCA808X_LED_BLINK_FREQ_128HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
+-#define QCA808X_LED_BLINK_FREQ_256HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
+-#define QCA808X_LED_BLINK_DUTY_MASK           GENMASK(2, 0)
+-#define QCA808X_LED_BLINK_DUTY_50_50          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
+-#define QCA808X_LED_BLINK_DUTY_75_25          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
+-#define QCA808X_LED_BLINK_DUTY_25_75          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
+-#define QCA808X_LED_BLINK_DUTY_33_67          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
+-#define QCA808X_LED_BLINK_DUTY_67_33          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
+-#define QCA808X_LED_BLINK_DUTY_17_83          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
+-#define QCA808X_LED_BLINK_DUTY_83_17          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
+-#define QCA808X_LED_BLINK_DUTY_8_92           FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
+-
+-#define QCA808X_MMD7_LED2_CTRL                        0x8074
+-#define QCA808X_MMD7_LED2_FORCE_CTRL          0x8075
+-#define QCA808X_MMD7_LED1_CTRL                        0x8076
+-#define QCA808X_MMD7_LED1_FORCE_CTRL          0x8077
+-#define QCA808X_MMD7_LED0_CTRL                        0x8078
+-#define QCA808X_MMD7_LED_CTRL(x)              (0x8078 - ((x) * 2))
+-
+-/* LED hw control pattern is the same for every LED */
+-#define QCA808X_LED_PATTERN_MASK              GENMASK(15, 0)
+-#define QCA808X_LED_SPEED2500_ON              BIT(15)
+-#define QCA808X_LED_SPEED2500_BLINK           BIT(14)
+-/* Follow blink trigger even if duplex or speed condition doesn't match */
+-#define QCA808X_LED_BLINK_CHECK_BYPASS                BIT(13)
+-#define QCA808X_LED_FULL_DUPLEX_ON            BIT(12)
+-#define QCA808X_LED_HALF_DUPLEX_ON            BIT(11)
+-#define QCA808X_LED_TX_BLINK                  BIT(10)
+-#define QCA808X_LED_RX_BLINK                  BIT(9)
+-#define QCA808X_LED_TX_ON_10MS                        BIT(8)
+-#define QCA808X_LED_RX_ON_10MS                        BIT(7)
+-#define QCA808X_LED_SPEED1000_ON              BIT(6)
+-#define QCA808X_LED_SPEED100_ON                       BIT(5)
+-#define QCA808X_LED_SPEED10_ON                        BIT(4)
+-#define QCA808X_LED_COLLISION_BLINK           BIT(3)
+-#define QCA808X_LED_SPEED1000_BLINK           BIT(2)
+-#define QCA808X_LED_SPEED100_BLINK            BIT(1)
+-#define QCA808X_LED_SPEED10_BLINK             BIT(0)
+-
+-#define QCA808X_MMD7_LED0_FORCE_CTRL          0x8079
+-#define QCA808X_MMD7_LED_FORCE_CTRL(x)                (0x8079 - ((x) * 2))
+-
+-/* LED force ctrl is the same for every LED
+- * No documentation exist for this, not even internal one
+- * with NDA as QCOM gives only info about configuring
+- * hw control pattern rules and doesn't indicate any way
+- * to force the LED to specific mode.
+- * These define comes from reverse and testing and maybe
+- * lack of some info or some info are not entirely correct.
+- * For the basic LED control and hw control these finding
+- * are enough to support LED control in all the required APIs.
+- *
+- * On doing some comparison with implementation with qca807x,
+- * it was found that it's 1:1 equal to it and confirms all the
+- * reverse done. It was also found further specification with the
+- * force mode and the blink modes.
+- */
+-#define QCA808X_LED_FORCE_EN                  BIT(15)
+-#define QCA808X_LED_FORCE_MODE_MASK           GENMASK(14, 13)
+-#define QCA808X_LED_FORCE_BLINK_1             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
+-#define QCA808X_LED_FORCE_BLINK_2             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
+-#define QCA808X_LED_FORCE_ON                  FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
+-#define QCA808X_LED_FORCE_OFF                 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
+-
+-#define QCA808X_MMD7_LED_POLARITY_CTRL                0x901a
+-/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
+- * LED to active high. It's not clear what BIT 3 and BIT 4 does.
+- */
+-#define QCA808X_LED_ACTIVE_HIGH                       BIT(6)
+-
+-/* QCA808X 1G chip type */
+-#define QCA808X_PHY_MMD7_CHIP_TYPE            0x901d
+-#define QCA808X_PHY_CHIP_TYPE_1G              BIT(0)
+-
+-#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL     0x9072
+-#define QCA8081_PHY_FIFO_RSTN                 BIT(11)
+-
+-MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
+-MODULE_AUTHOR("Matus Ujhelyi");
+-MODULE_LICENSE("GPL");
+-
+-enum stat_access_type {
+-      PHY,
+-      MMD
+-};
+-
+-struct at803x_hw_stat {
+-      const char *string;
+-      u8 reg;
+-      u32 mask;
+-      enum stat_access_type access_type;
+-};
+-
+-static struct at803x_hw_stat qca83xx_hw_stats[] = {
+-      { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
+-      { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
+-      { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
+-};
+-
+-struct at803x_ss_mask {
+-      u16 speed_mask;
+-      u8 speed_shift;
+-};
+-
+-struct at803x_priv {
+-      int flags;
+-      u16 clk_25m_reg;
+-      u16 clk_25m_mask;
+-      u8 smarteee_lpi_tw_1g;
+-      u8 smarteee_lpi_tw_100m;
+-      bool is_fiber;
+-      bool is_1000basex;
+-      struct regulator_dev *vddio_rdev;
+-      struct regulator_dev *vddh_rdev;
+-      u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
+-      int led_polarity_mode;
+-};
+-
+-struct at803x_context {
+-      u16 bmcr;
+-      u16 advertise;
+-      u16 control1000;
+-      u16 int_enable;
+-      u16 smart_speed;
+-      u16 led_control;
+-};
+-
+-static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
+-{
+-      int ret;
+-
+-      ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
+-      if (ret < 0)
+-              return ret;
+-
+-      return phy_write(phydev, AT803X_DEBUG_DATA, data);
+-}
+-
+-static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
+-{
+-      int ret;
+-
+-      ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
+-      if (ret < 0)
+-              return ret;
+-
+-      return phy_read(phydev, AT803X_DEBUG_DATA);
+-}
+-
+-static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
+-                               u16 clear, u16 set)
+-{
+-      u16 val;
+-      int ret;
+-
+-      ret = at803x_debug_reg_read(phydev, reg);
+-      if (ret < 0)
+-              return ret;
+-
+-      val = ret & 0xffff;
+-      val &= ~clear;
+-      val |= set;
+-
+-      return phy_write(phydev, AT803X_DEBUG_DATA, val);
+-}
+-
+-static int at803x_write_page(struct phy_device *phydev, int page)
+-{
+-      int mask;
+-      int set;
+-
+-      if (page == AT803X_PAGE_COPPER) {
+-              set = AT803X_BT_BX_REG_SEL;
+-              mask = 0;
+-      } else {
+-              set = 0;
+-              mask = AT803X_BT_BX_REG_SEL;
+-      }
+-
+-      return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set);
+-}
+-
+-static int at803x_read_page(struct phy_device *phydev)
+-{
+-      int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG);
+-
+-      if (ccr < 0)
+-              return ccr;
+-
+-      if (ccr & AT803X_BT_BX_REG_SEL)
+-              return AT803X_PAGE_COPPER;
+-
+-      return AT803X_PAGE_FIBER;
+-}
+-
+-static int at803x_enable_rx_delay(struct phy_device *phydev)
+-{
+-      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_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_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_SYSTEM_CTRL_MODE,
+-                                   AT803X_DEBUG_TX_CLK_DLY_EN, 0);
+-}
+-
+-/* save relevant PHY registers to private copy */
+-static void at803x_context_save(struct phy_device *phydev,
+-                              struct at803x_context *context)
+-{
+-      context->bmcr = phy_read(phydev, MII_BMCR);
+-      context->advertise = phy_read(phydev, MII_ADVERTISE);
+-      context->control1000 = phy_read(phydev, MII_CTRL1000);
+-      context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
+-      context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
+-      context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
+-}
+-
+-/* restore relevant PHY registers from private copy */
+-static void at803x_context_restore(struct phy_device *phydev,
+-                                 const struct at803x_context *context)
+-{
+-      phy_write(phydev, MII_BMCR, context->bmcr);
+-      phy_write(phydev, MII_ADVERTISE, context->advertise);
+-      phy_write(phydev, MII_CTRL1000, context->control1000);
+-      phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
+-      phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
+-      phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
+-}
+-
+-static int at803x_set_wol(struct phy_device *phydev,
+-                        struct ethtool_wolinfo *wol)
+-{
+-      int ret, irq_enabled;
+-
+-      if (wol->wolopts & WAKE_MAGIC) {
+-              struct net_device *ndev = phydev->attached_dev;
+-              const u8 *mac;
+-              unsigned int i;
+-              static const unsigned int offsets[] = {
+-                      AT803X_LOC_MAC_ADDR_32_47_OFFSET,
+-                      AT803X_LOC_MAC_ADDR_16_31_OFFSET,
+-                      AT803X_LOC_MAC_ADDR_0_15_OFFSET,
+-              };
+-
+-              if (!ndev)
+-                      return -ENODEV;
+-
+-              mac = (const u8 *)ndev->dev_addr;
+-
+-              if (!is_valid_ether_addr(mac))
+-                      return -EINVAL;
+-
+-              for (i = 0; i < 3; i++)
+-                      phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
+-                                    mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
+-
+-              /* Enable WOL interrupt */
+-              ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
+-              if (ret)
+-                      return ret;
+-      } else {
+-              /* Disable WOL interrupt */
+-              ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
+-              if (ret)
+-                      return ret;
+-      }
+-
+-      /* Clear WOL status */
+-      ret = phy_read(phydev, AT803X_INTR_STATUS);
+-      if (ret < 0)
+-              return ret;
+-
+-      /* Check if there are other interrupts except for WOL triggered when PHY is
+-       * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
+-       * be passed up to the interrupt PIN.
+-       */
+-      irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
+-      if (irq_enabled < 0)
+-              return irq_enabled;
+-
+-      irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
+-      if (ret & irq_enabled && !phy_polling_mode(phydev))
+-              phy_trigger_machine(phydev);
+-
+-      return 0;
+-}
+-
+-static void at803x_get_wol(struct phy_device *phydev,
+-                         struct ethtool_wolinfo *wol)
+-{
+-      int value;
+-
+-      wol->supported = WAKE_MAGIC;
+-      wol->wolopts = 0;
+-
+-      value = phy_read(phydev, AT803X_INTR_ENABLE);
+-      if (value < 0)
+-              return;
+-
+-      if (value & AT803X_INTR_ENABLE_WOL)
+-              wol->wolopts |= WAKE_MAGIC;
+-}
+-
+-static int qca83xx_get_sset_count(struct phy_device *phydev)
+-{
+-      return ARRAY_SIZE(qca83xx_hw_stats);
+-}
+-
+-static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
+-{
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
+-              strscpy(data + i * ETH_GSTRING_LEN,
+-                      qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
+-      }
+-}
+-
+-static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
+-{
+-      struct at803x_hw_stat stat = qca83xx_hw_stats[i];
+-      struct at803x_priv *priv = phydev->priv;
+-      int val;
+-      u64 ret;
+-
+-      if (stat.access_type == MMD)
+-              val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
+-      else
+-              val = phy_read(phydev, stat.reg);
+-
+-      if (val < 0) {
+-              ret = U64_MAX;
+-      } else {
+-              val = val & stat.mask;
+-              priv->stats[i] += val;
+-              ret = priv->stats[i];
+-      }
+-
+-      return ret;
+-}
+-
+-static void qca83xx_get_stats(struct phy_device *phydev,
+-                            struct ethtool_stats *stats, u64 *data)
+-{
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
+-              data[i] = qca83xx_get_stat(phydev, i);
+-}
+-
+-static int at803x_suspend(struct phy_device *phydev)
+-{
+-      int value;
+-      int wol_enabled;
+-
+-      value = phy_read(phydev, AT803X_INTR_ENABLE);
+-      wol_enabled = value & AT803X_INTR_ENABLE_WOL;
+-
+-      if (wol_enabled)
+-              value = BMCR_ISOLATE;
+-      else
+-              value = BMCR_PDOWN;
+-
+-      phy_modify(phydev, MII_BMCR, 0, value);
+-
+-      return 0;
+-}
+-
+-static int at803x_resume(struct phy_device *phydev)
+-{
+-      return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
+-}
+-
+-static int at803x_parse_dt(struct phy_device *phydev)
+-{
+-      struct device_node *node = phydev->mdio.dev.of_node;
+-      struct at803x_priv *priv = phydev->priv;
+-      u32 freq, strength, tw;
+-      unsigned int sel;
+-      int ret;
+-
+-      if (!IS_ENABLED(CONFIG_OF_MDIO))
+-              return 0;
+-
+-      if (of_property_read_bool(node, "qca,disable-smarteee"))
+-              priv->flags |= AT803X_DISABLE_SMARTEEE;
+-
+-      if (of_property_read_bool(node, "qca,disable-hibernation-mode"))
+-              priv->flags |= AT803X_DISABLE_HIBERNATION_MODE;
+-
+-      if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) {
+-              if (!tw || tw > 255) {
+-                      phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n");
+-                      return -EINVAL;
+-              }
+-              priv->smarteee_lpi_tw_1g = tw;
+-      }
+-
+-      if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) {
+-              if (!tw || tw > 255) {
+-                      phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n");
+-                      return -EINVAL;
+-              }
+-              priv->smarteee_lpi_tw_100m = tw;
+-      }
+-
+-      ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq);
+-      if (!ret) {
+-              switch (freq) {
+-              case 25000000:
+-                      sel = AT803X_CLK_OUT_25MHZ_XTAL;
+-                      break;
+-              case 50000000:
+-                      sel = AT803X_CLK_OUT_50MHZ_PLL;
+-                      break;
+-              case 62500000:
+-                      sel = AT803X_CLK_OUT_62_5MHZ_PLL;
+-                      break;
+-              case 125000000:
+-                      sel = AT803X_CLK_OUT_125MHZ_PLL;
+-                      break;
+-              default:
+-                      phydev_err(phydev, "invalid qca,clk-out-frequency\n");
+-                      return -EINVAL;
+-              }
+-
+-              priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
+-              priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
+-      }
+-
+-      ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
+-      if (!ret) {
+-              priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK;
+-              switch (strength) {
+-              case AR803X_STRENGTH_FULL:
+-                      priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL;
+-                      break;
+-              case AR803X_STRENGTH_HALF:
+-                      priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF;
+-                      break;
+-              case AR803X_STRENGTH_QUARTER:
+-                      priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER;
+-                      break;
+-              default:
+-                      phydev_err(phydev, "invalid qca,clk-out-strength\n");
+-                      return -EINVAL;
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+-static int at803x_probe(struct phy_device *phydev)
+-{
+-      struct device *dev = &phydev->mdio.dev;
+-      struct at803x_priv *priv;
+-      int ret;
+-
+-      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+-      if (!priv)
+-              return -ENOMEM;
+-
+-      /* Init LED polarity mode to -1 */
+-      priv->led_polarity_mode = -1;
+-
+-      phydev->priv = priv;
+-
+-      ret = at803x_parse_dt(phydev);
+-      if (ret)
+-              return ret;
+-
+-      return 0;
+-}
+-
+-static int at803x_get_features(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      int err;
+-
+-      err = genphy_read_abilities(phydev);
+-      if (err)
+-              return err;
+-
+-      if (phydev->drv->phy_id != ATH8031_PHY_ID)
+-              return 0;
+-
+-      /* AR8031/AR8033 have different status registers
+-       * for copper and fiber operation. However, the
+-       * extended status register is the same for both
+-       * operation modes.
+-       *
+-       * As a result of that, ESTATUS_1000_XFULL is set
+-       * to 1 even when operating in copper TP mode.
+-       *
+-       * Remove this mode from the supported link modes
+-       * when not operating in 1000BaseX mode.
+-       */
+-      if (!priv->is_1000basex)
+-              linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+-                                 phydev->supported);
+-
+-      return 0;
+-}
+-
+-static int at803x_smarteee_config(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      u16 mask = 0, val = 0;
+-      int ret;
+-
+-      if (priv->flags & AT803X_DISABLE_SMARTEEE)
+-              return phy_modify_mmd(phydev, MDIO_MMD_PCS,
+-                                    AT803X_MMD3_SMARTEEE_CTL3,
+-                                    AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0);
+-
+-      if (priv->smarteee_lpi_tw_1g) {
+-              mask |= 0xff00;
+-              val |= priv->smarteee_lpi_tw_1g << 8;
+-      }
+-      if (priv->smarteee_lpi_tw_100m) {
+-              mask |= 0x00ff;
+-              val |= priv->smarteee_lpi_tw_100m;
+-      }
+-      if (!mask)
+-              return 0;
+-
+-      ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1,
+-                           mask, val);
+-      if (ret)
+-              return ret;
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3,
+-                            AT803X_MMD3_SMARTEEE_CTL3_LPI_EN,
+-                            AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
+-}
+-
+-static int at803x_clk_out_config(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-
+-      if (!priv->clk_25m_mask)
+-              return 0;
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M,
+-                            priv->clk_25m_mask, priv->clk_25m_reg);
+-}
+-
+-static int at8031_pll_config(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-
+-      /* The default after hardware reset is PLL OFF. After a soft reset, the
+-       * values are retained.
+-       */
+-      if (priv->flags & AT803X_KEEP_PLL_ENABLED)
+-              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+-                                           0, AT803X_DEBUG_PLL_ON);
+-      else
+-              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+-                                           AT803X_DEBUG_PLL_ON, 0);
+-}
+-
+-static int at803x_hibernation_mode_config(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-
+-      /* The default after hardware reset is hibernation mode enabled. After
+-       * software reset, the value is retained.
+-       */
+-      if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE))
+-              return 0;
+-
+-      return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
+-                                       AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0);
+-}
+-
+-static int at803x_config_init(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      /* The RX and TX delay default is:
+-       *   after HW reset: RX delay enabled and TX delay disabled
+-       *   after SW reset: RX delay enabled, while TX delay retains the
+-       *   value before reset.
+-       */
+-      if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+-          phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+-              ret = at803x_enable_rx_delay(phydev);
+-      else
+-              ret = at803x_disable_rx_delay(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+-          phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+-              ret = at803x_enable_tx_delay(phydev);
+-      else
+-              ret = at803x_disable_tx_delay(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = at803x_smarteee_config(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = at803x_clk_out_config(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = at803x_hibernation_mode_config(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      /* Ar803x extended next page bit is enabled by default. Cisco
+-       * multigig switches read this bit and attempt to negotiate 10Gbps
+-       * rates even if the next page bit is disabled. This is incorrect
+-       * behaviour but we still need to accommodate it. XNP is only needed
+-       * for 10Gbps support, so disable XNP.
+-       */
+-      return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
+-}
+-
+-static int at803x_ack_interrupt(struct phy_device *phydev)
+-{
+-      int err;
+-
+-      err = phy_read(phydev, AT803X_INTR_STATUS);
+-
+-      return (err < 0) ? err : 0;
+-}
+-
+-static int at803x_config_intr(struct phy_device *phydev)
+-{
+-      int err;
+-      int value;
+-
+-      value = phy_read(phydev, AT803X_INTR_ENABLE);
+-
+-      if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+-              /* Clear any pending interrupts */
+-              err = at803x_ack_interrupt(phydev);
+-              if (err)
+-                      return err;
+-
+-              value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
+-              value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
+-              value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
+-              value |= AT803X_INTR_ENABLE_LINK_FAIL;
+-              value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
+-
+-              err = phy_write(phydev, AT803X_INTR_ENABLE, value);
+-      } else {
+-              err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
+-              if (err)
+-                      return err;
+-
+-              /* Clear any pending interrupts */
+-              err = at803x_ack_interrupt(phydev);
+-      }
+-
+-      return err;
+-}
+-
+-static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
+-{
+-      int irq_status, int_enabled;
+-
+-      irq_status = phy_read(phydev, AT803X_INTR_STATUS);
+-      if (irq_status < 0) {
+-              phy_error(phydev);
+-              return IRQ_NONE;
+-      }
+-
+-      /* Read the current enabled interrupts */
+-      int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
+-      if (int_enabled < 0) {
+-              phy_error(phydev);
+-              return IRQ_NONE;
+-      }
+-
+-      /* See if this was one of our enabled interrupts */
+-      if (!(irq_status & int_enabled))
+-              return IRQ_NONE;
+-
+-      phy_trigger_machine(phydev);
+-
+-      return IRQ_HANDLED;
+-}
+-
+-static void at803x_link_change_notify(struct phy_device *phydev)
+-{
+-      /*
+-       * Conduct a hardware reset for AT8030 every time a link loss is
+-       * signalled. This is necessary to circumvent a hardware bug that
+-       * occurs when the cable is unplugged while TX packets are pending
+-       * in the FIFO. In such cases, the FIFO enters an error mode it
+-       * cannot recover from by software.
+-       */
+-      if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) {
+-              struct at803x_context context;
+-
+-              at803x_context_save(phydev, &context);
+-
+-              phy_device_reset(phydev, 1);
+-              usleep_range(1000, 2000);
+-              phy_device_reset(phydev, 0);
+-              usleep_range(1000, 2000);
+-
+-              at803x_context_restore(phydev, &context);
+-
+-              phydev_dbg(phydev, "%s(): phy was reset\n", __func__);
+-      }
+-}
+-
+-static int at803x_read_specific_status(struct phy_device *phydev,
+-                                     struct at803x_ss_mask ss_mask)
+-{
+-      int ss;
+-
+-      /* Read the AT8035 PHY-Specific Status register, which indicates the
+-       * speed and duplex that the PHY is actually using, irrespective of
+-       * whether we are in autoneg mode or not.
+-       */
+-      ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
+-      if (ss < 0)
+-              return ss;
+-
+-      if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
+-              int sfc, speed;
+-
+-              sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
+-              if (sfc < 0)
+-                      return sfc;
+-
+-              speed = ss & ss_mask.speed_mask;
+-              speed >>= ss_mask.speed_shift;
+-
+-              switch (speed) {
+-              case AT803X_SS_SPEED_10:
+-                      phydev->speed = SPEED_10;
+-                      break;
+-              case AT803X_SS_SPEED_100:
+-                      phydev->speed = SPEED_100;
+-                      break;
+-              case AT803X_SS_SPEED_1000:
+-                      phydev->speed = SPEED_1000;
+-                      break;
+-              case QCA808X_SS_SPEED_2500:
+-                      phydev->speed = SPEED_2500;
+-                      break;
+-              }
+-              if (ss & AT803X_SS_DUPLEX)
+-                      phydev->duplex = DUPLEX_FULL;
+-              else
+-                      phydev->duplex = DUPLEX_HALF;
+-
+-              if (ss & AT803X_SS_MDIX)
+-                      phydev->mdix = ETH_TP_MDI_X;
+-              else
+-                      phydev->mdix = ETH_TP_MDI;
+-
+-              switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
+-              case AT803X_SFC_MANUAL_MDI:
+-                      phydev->mdix_ctrl = ETH_TP_MDI;
+-                      break;
+-              case AT803X_SFC_MANUAL_MDIX:
+-                      phydev->mdix_ctrl = ETH_TP_MDI_X;
+-                      break;
+-              case AT803X_SFC_AUTOMATIC_CROSSOVER:
+-                      phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
+-                      break;
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+-static int at803x_read_status(struct phy_device *phydev)
+-{
+-      struct at803x_ss_mask ss_mask = { 0 };
+-      int err, old_link = phydev->link;
+-
+-      /* Update the link, but return if there was an error */
+-      err = genphy_update_link(phydev);
+-      if (err)
+-              return err;
+-
+-      /* why bother the PHY if nothing can have changed */
+-      if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+-              return 0;
+-
+-      phydev->speed = SPEED_UNKNOWN;
+-      phydev->duplex = DUPLEX_UNKNOWN;
+-      phydev->pause = 0;
+-      phydev->asym_pause = 0;
+-
+-      err = genphy_read_lpa(phydev);
+-      if (err < 0)
+-              return err;
+-
+-      ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
+-      ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
+-      err = at803x_read_specific_status(phydev, ss_mask);
+-      if (err < 0)
+-              return err;
+-
+-      if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
+-              phy_resolve_aneg_pause(phydev);
+-
+-      return 0;
+-}
+-
+-static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
+-{
+-      u16 val;
+-
+-      switch (ctrl) {
+-      case ETH_TP_MDI:
+-              val = AT803X_SFC_MANUAL_MDI;
+-              break;
+-      case ETH_TP_MDI_X:
+-              val = AT803X_SFC_MANUAL_MDIX;
+-              break;
+-      case ETH_TP_MDI_AUTO:
+-              val = AT803X_SFC_AUTOMATIC_CROSSOVER;
+-              break;
+-      default:
+-              return 0;
+-      }
+-
+-      return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
+-                        AT803X_SFC_MDI_CROSSOVER_MODE_M,
+-                        FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
+-}
+-
+-static int at803x_prepare_config_aneg(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
+-      if (ret < 0)
+-              return ret;
+-
+-      /* Changes of the midx bits are disruptive to the normal operation;
+-       * therefore any changes to these registers must be followed by a
+-       * software reset to take effect.
+-       */
+-      if (ret == 1) {
+-              ret = genphy_soft_reset(phydev);
+-              if (ret < 0)
+-                      return ret;
+-      }
+-
+-      return 0;
+-}
+-
+-static int at803x_config_aneg(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      int ret;
+-
+-      ret = at803x_prepare_config_aneg(phydev);
+-      if (ret)
+-              return ret;
+-
+-      if (priv->is_1000basex)
+-              return genphy_c37_config_aneg(phydev);
+-
+-      return genphy_config_aneg(phydev);
+-}
+-
+-static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
+-{
+-      int val;
+-
+-      val = phy_read(phydev, AT803X_SMART_SPEED);
+-      if (val < 0)
+-              return val;
+-
+-      if (val & AT803X_SMART_SPEED_ENABLE)
+-              *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
+-      else
+-              *d = DOWNSHIFT_DEV_DISABLE;
+-
+-      return 0;
+-}
+-
+-static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
+-{
+-      u16 mask, set;
+-      int ret;
+-
+-      switch (cnt) {
+-      case DOWNSHIFT_DEV_DEFAULT_COUNT:
+-              cnt = AT803X_DEFAULT_DOWNSHIFT;
+-              fallthrough;
+-      case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
+-              set = AT803X_SMART_SPEED_ENABLE |
+-                    AT803X_SMART_SPEED_BYPASS_TIMER |
+-                    FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
+-              mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
+-              break;
+-      case DOWNSHIFT_DEV_DISABLE:
+-              set = 0;
+-              mask = AT803X_SMART_SPEED_ENABLE |
+-                     AT803X_SMART_SPEED_BYPASS_TIMER;
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
+-
+-      /* After changing the smart speed settings, we need to perform a
+-       * software reset, use phy_init_hw() to make sure we set the
+-       * reapply any values which might got lost during software reset.
+-       */
+-      if (ret == 1)
+-              ret = phy_init_hw(phydev);
+-
+-      return ret;
+-}
+-
+-static int at803x_get_tunable(struct phy_device *phydev,
+-                            struct ethtool_tunable *tuna, void *data)
+-{
+-      switch (tuna->id) {
+-      case ETHTOOL_PHY_DOWNSHIFT:
+-              return at803x_get_downshift(phydev, data);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+-static int at803x_set_tunable(struct phy_device *phydev,
+-                            struct ethtool_tunable *tuna, const void *data)
+-{
+-      switch (tuna->id) {
+-      case ETHTOOL_PHY_DOWNSHIFT:
+-              return at803x_set_downshift(phydev, *(const u8 *)data);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+-static int at803x_cable_test_result_trans(u16 status)
+-{
+-      switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
+-      case AT803X_CDT_STATUS_STAT_NORMAL:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+-      case AT803X_CDT_STATUS_STAT_SHORT:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+-      case AT803X_CDT_STATUS_STAT_OPEN:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
+-      case AT803X_CDT_STATUS_STAT_FAIL:
+-      default:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+-      }
+-}
+-
+-static bool at803x_cdt_test_failed(u16 status)
+-{
+-      return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) ==
+-              AT803X_CDT_STATUS_STAT_FAIL;
+-}
+-
+-static bool at803x_cdt_fault_length_valid(u16 status)
+-{
+-      switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
+-      case AT803X_CDT_STATUS_STAT_OPEN:
+-      case AT803X_CDT_STATUS_STAT_SHORT:
+-              return true;
+-      }
+-      return false;
+-}
+-
+-static int at803x_cdt_fault_length(int dt)
+-{
+-      /* According to the datasheet the distance to the fault is
+-       * DELTA_TIME * 0.824 meters.
+-       *
+-       * The author suspect the correct formula is:
+-       *
+-       *   fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
+-       *
+-       * where c is the speed of light, VF is the velocity factor of
+-       * the twisted pair cable, 125MHz the counter frequency and
+-       * we need to divide by 2 because the hardware will measure the
+-       * round trip time to the fault and back to the PHY.
+-       *
+-       * With a VF of 0.69 we get the factor 0.824 mentioned in the
+-       * datasheet.
+-       */
+-      return (dt * 824) / 10;
+-}
+-
+-static int at803x_cdt_start(struct phy_device *phydev,
+-                          u32 cdt_start)
+-{
+-      return phy_write(phydev, AT803X_CDT, cdt_start);
+-}
+-
+-static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
+-                                        u32 cdt_en)
+-{
+-      int val, ret;
+-
+-      /* One test run takes about 25ms */
+-      ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
+-                                  !(val & cdt_en),
+-                                  30000, 100000, true);
+-
+-      return ret < 0 ? ret : 0;
+-}
+-
+-static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair)
+-{
+-      static const int ethtool_pair[] = {
+-              ETHTOOL_A_CABLE_PAIR_A,
+-              ETHTOOL_A_CABLE_PAIR_B,
+-              ETHTOOL_A_CABLE_PAIR_C,
+-              ETHTOOL_A_CABLE_PAIR_D,
+-      };
+-      int ret, val;
+-
+-      val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
+-            AT803X_CDT_ENABLE_TEST;
+-      ret = at803x_cdt_start(phydev, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST);
+-      if (ret)
+-              return ret;
+-
+-      val = phy_read(phydev, AT803X_CDT_STATUS);
+-      if (val < 0)
+-              return val;
+-
+-      if (at803x_cdt_test_failed(val))
+-              return 0;
+-
+-      ethnl_cable_test_result(phydev, ethtool_pair[pair],
+-                              at803x_cable_test_result_trans(val));
+-
+-      if (at803x_cdt_fault_length_valid(val)) {
+-              val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val);
+-              ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
+-                                            at803x_cdt_fault_length(val));
+-      }
+-
+-      return 1;
+-}
+-
+-static int at803x_cable_test_get_status(struct phy_device *phydev,
+-                                      bool *finished, unsigned long pair_mask)
+-{
+-      int retries = 20;
+-      int pair, ret;
+-
+-      *finished = false;
+-
+-      /* According to the datasheet the CDT can be performed when
+-       * there is no link partner or when the link partner is
+-       * auto-negotiating. Starting the test will restart the AN
+-       * automatically. It seems that doing this repeatedly we will
+-       * get a slot where our link partner won't disturb our
+-       * measurement.
+-       */
+-      while (pair_mask && retries--) {
+-              for_each_set_bit(pair, &pair_mask, 4) {
+-                      ret = at803x_cable_test_one_pair(phydev, pair);
+-                      if (ret < 0)
+-                              return ret;
+-                      if (ret)
+-                              clear_bit(pair, &pair_mask);
+-              }
+-              if (pair_mask)
+-                      msleep(250);
+-      }
+-
+-      *finished = true;
+-
+-      return 0;
+-}
+-
+-static void at803x_cable_test_autoneg(struct phy_device *phydev)
+-{
+-      /* Enable auto-negotiation, but advertise no capabilities, no link
+-       * will be established. A restart of the auto-negotiation is not
+-       * required, because the cable test will automatically break the link.
+-       */
+-      phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
+-      phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
+-}
+-
+-static int at803x_cable_test_start(struct phy_device *phydev)
+-{
+-      at803x_cable_test_autoneg(phydev);
+-      /* we do all the (time consuming) work later */
+-      return 0;
+-}
+-
+-static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
+-                                          unsigned int selector)
+-{
+-      struct phy_device *phydev = rdev_get_drvdata(rdev);
+-
+-      if (selector)
+-              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+-                                           0, AT803X_DEBUG_RGMII_1V8);
+-      else
+-              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+-                                           AT803X_DEBUG_RGMII_1V8, 0);
+-}
+-
+-static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
+-{
+-      struct phy_device *phydev = rdev_get_drvdata(rdev);
+-      int val;
+-
+-      val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
+-      if (val < 0)
+-              return val;
+-
+-      return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
+-}
+-
+-static const struct regulator_ops vddio_regulator_ops = {
+-      .list_voltage = regulator_list_voltage_table,
+-      .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
+-      .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
+-};
+-
+-static const unsigned int vddio_voltage_table[] = {
+-      1500000,
+-      1800000,
+-};
+-
+-static const struct regulator_desc vddio_desc = {
+-      .name = "vddio",
+-      .of_match = of_match_ptr("vddio-regulator"),
+-      .n_voltages = ARRAY_SIZE(vddio_voltage_table),
+-      .volt_table = vddio_voltage_table,
+-      .ops = &vddio_regulator_ops,
+-      .type = REGULATOR_VOLTAGE,
+-      .owner = THIS_MODULE,
+-};
+-
+-static const struct regulator_ops vddh_regulator_ops = {
+-};
+-
+-static const struct regulator_desc vddh_desc = {
+-      .name = "vddh",
+-      .of_match = of_match_ptr("vddh-regulator"),
+-      .n_voltages = 1,
+-      .fixed_uV = 2500000,
+-      .ops = &vddh_regulator_ops,
+-      .type = REGULATOR_VOLTAGE,
+-      .owner = THIS_MODULE,
+-};
+-
+-static int at8031_register_regulators(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      struct device *dev = &phydev->mdio.dev;
+-      struct regulator_config config = { };
+-
+-      config.dev = dev;
+-      config.driver_data = phydev;
+-
+-      priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
+-      if (IS_ERR(priv->vddio_rdev)) {
+-              phydev_err(phydev, "failed to register VDDIO regulator\n");
+-              return PTR_ERR(priv->vddio_rdev);
+-      }
+-
+-      priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
+-      if (IS_ERR(priv->vddh_rdev)) {
+-              phydev_err(phydev, "failed to register VDDH regulator\n");
+-              return PTR_ERR(priv->vddh_rdev);
+-      }
+-
+-      return 0;
+-}
+-
+-static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
+-{
+-      struct phy_device *phydev = upstream;
+-      __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
+-      __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
+-      DECLARE_PHY_INTERFACE_MASK(interfaces);
+-      phy_interface_t iface;
+-
+-      linkmode_zero(phy_support);
+-      phylink_set(phy_support, 1000baseX_Full);
+-      phylink_set(phy_support, 1000baseT_Full);
+-      phylink_set(phy_support, Autoneg);
+-      phylink_set(phy_support, Pause);
+-      phylink_set(phy_support, Asym_Pause);
+-
+-      linkmode_zero(sfp_support);
+-      sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
+-      /* Some modules support 10G modes as well as others we support.
+-       * Mask out non-supported modes so the correct interface is picked.
+-       */
+-      linkmode_and(sfp_support, phy_support, sfp_support);
+-
+-      if (linkmode_empty(sfp_support)) {
+-              dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
+-              return -EINVAL;
+-      }
+-
+-      iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
+-
+-      /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
+-       * interface for use with SFP modules.
+-       * However, some copper modules detected as having a preferred SGMII
+-       * interface do default to and function in 1000Base-X mode, so just
+-       * print a warning and allow such modules, as they may have some chance
+-       * of working.
+-       */
+-      if (iface == PHY_INTERFACE_MODE_SGMII)
+-              dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
+-      else if (iface != PHY_INTERFACE_MODE_1000BASEX)
+-              return -EINVAL;
+-
+-      return 0;
+-}
+-
+-static const struct sfp_upstream_ops at8031_sfp_ops = {
+-      .attach = phy_sfp_attach,
+-      .detach = phy_sfp_detach,
+-      .module_insert = at8031_sfp_insert,
+-};
+-
+-static int at8031_parse_dt(struct phy_device *phydev)
+-{
+-      struct device_node *node = phydev->mdio.dev.of_node;
+-      struct at803x_priv *priv = phydev->priv;
+-      int ret;
+-
+-      if (of_property_read_bool(node, "qca,keep-pll-enabled"))
+-              priv->flags |= AT803X_KEEP_PLL_ENABLED;
+-
+-      ret = at8031_register_regulators(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
+-                                               "vddio");
+-      if (ret) {
+-              phydev_err(phydev, "failed to get VDDIO regulator\n");
+-              return ret;
+-      }
+-
+-      /* Only AR8031/8033 support 1000Base-X for SFP modules */
+-      return phy_sfp_probe(phydev, &at8031_sfp_ops);
+-}
+-
+-static int at8031_probe(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      int mode_cfg;
+-      int ccr;
+-      int ret;
+-
+-      ret = at803x_probe(phydev);
+-      if (ret)
+-              return ret;
+-
+-      /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
+-       * options.
+-       */
+-      ret = at8031_parse_dt(phydev);
+-      if (ret)
+-              return ret;
+-
+-      ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
+-      if (ccr < 0)
+-              return ccr;
+-      mode_cfg = ccr & AT803X_MODE_CFG_MASK;
+-
+-      switch (mode_cfg) {
+-      case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
+-      case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
+-              priv->is_1000basex = true;
+-              fallthrough;
+-      case AT803X_MODE_CFG_FX100_RGMII_50OHM:
+-      case AT803X_MODE_CFG_FX100_RGMII_75OHM:
+-              priv->is_fiber = true;
+-              break;
+-      }
+-
+-      /* Disable WoL in 1588 register which is enabled
+-       * by default
+-       */
+-      return phy_modify_mmd(phydev, MDIO_MMD_PCS,
+-                            AT803X_PHY_MMD3_WOL_CTRL,
+-                            AT803X_WOL_EN, 0);
+-}
+-
+-static int at8031_config_init(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      int ret;
+-
+-      /* Some bootloaders leave the fiber page selected.
+-       * Switch to the appropriate page (fiber or copper), as otherwise we
+-       * read the PHY capabilities from the wrong page.
+-       */
+-      phy_lock_mdio_bus(phydev);
+-      ret = at803x_write_page(phydev,
+-                              priv->is_fiber ? AT803X_PAGE_FIBER :
+-                                               AT803X_PAGE_COPPER);
+-      phy_unlock_mdio_bus(phydev);
+-      if (ret)
+-              return ret;
+-
+-      ret = at8031_pll_config(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      return at803x_config_init(phydev);
+-}
+-
+-static int at8031_set_wol(struct phy_device *phydev,
+-                        struct ethtool_wolinfo *wol)
+-{
+-      int ret;
+-
+-      /* First setup MAC address and enable WOL interrupt */
+-      ret = at803x_set_wol(phydev, wol);
+-      if (ret)
+-              return ret;
+-
+-      if (wol->wolopts & WAKE_MAGIC)
+-              /* Enable WOL function for 1588 */
+-              ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
+-                                   AT803X_PHY_MMD3_WOL_CTRL,
+-                                   0, AT803X_WOL_EN);
+-      else
+-              /* Disable WoL function for 1588 */
+-              ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
+-                                   AT803X_PHY_MMD3_WOL_CTRL,
+-                                   AT803X_WOL_EN, 0);
+-
+-      return ret;
+-}
+-
+-static int at8031_config_intr(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      int err, value = 0;
+-
+-      if (phydev->interrupts == PHY_INTERRUPT_ENABLED &&
+-          priv->is_fiber) {
+-              /* Clear any pending interrupts */
+-              err = at803x_ack_interrupt(phydev);
+-              if (err)
+-                      return err;
+-
+-              value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
+-              value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
+-
+-              err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value);
+-              if (err)
+-                      return err;
+-      }
+-
+-      return at803x_config_intr(phydev);
+-}
+-
+-/* AR8031 and AR8033 share the same read status logic */
+-static int at8031_read_status(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-
+-      if (priv->is_1000basex)
+-              return genphy_c37_read_status(phydev);
+-
+-      return at803x_read_status(phydev);
+-}
+-
+-/* AR8031 and AR8035 share the same cable test get status reg */
+-static int at8031_cable_test_get_status(struct phy_device *phydev,
+-                                      bool *finished)
+-{
+-      return at803x_cable_test_get_status(phydev, finished, 0xf);
+-}
+-
+-/* AR8031 and AR8035 share the same cable test start logic */
+-static int at8031_cable_test_start(struct phy_device *phydev)
+-{
+-      at803x_cable_test_autoneg(phydev);
+-      phy_write(phydev, MII_CTRL1000, 0);
+-      /* we do all the (time consuming) work later */
+-      return 0;
+-}
+-
+-/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */
+-static int at8032_cable_test_get_status(struct phy_device *phydev,
+-                                      bool *finished)
+-{
+-      return at803x_cable_test_get_status(phydev, finished, 0x3);
+-}
+-
+-static int at8035_parse_dt(struct phy_device *phydev)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-
+-      /* Mask is set by the generic at803x_parse_dt
+-       * if property is set. Assume property is set
+-       * with the mask not zero.
+-       */
+-      if (priv->clk_25m_mask) {
+-              /* Fixup for the AR8030/AR8035. This chip has another mask and
+-               * doesn't support the DSP reference. Eg. the lowest bit of the
+-               * mask. The upper two bits select the same frequencies. Mask
+-               * the lowest bit here.
+-               *
+-               * Warning:
+-               *   There was no datasheet for the AR8030 available so this is
+-               *   just a guess. But the AR8035 is listed as pin compatible
+-               *   to the AR8030 so there might be a good chance it works on
+-               *   the AR8030 too.
+-               */
+-              priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
+-              priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
+-      }
+-
+-      return 0;
+-}
+-
+-/* AR8030 and AR8035 shared the same special mask for clk_25m */
+-static int at8035_probe(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      ret = at803x_probe(phydev);
+-      if (ret)
+-              return ret;
+-
+-      return at8035_parse_dt(phydev);
+-}
+-
+-static int qca83xx_config_init(struct phy_device *phydev)
+-{
+-      u8 switch_revision;
+-
+-      switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
+-
+-      switch (switch_revision) {
+-      case 1:
+-              /* For 100M waveform */
+-              at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
+-              /* Turn on Gigabit clock */
+-              at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
+-              break;
+-
+-      case 2:
+-              phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
+-              fallthrough;
+-      case 4:
+-              phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
+-              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;
+-      }
+-
+-      /* Following original QCA sourcecode set port to prefer master */
+-      phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
+-
+-      return 0;
+-}
+-
+-static int qca8327_config_init(struct phy_device *phydev)
+-{
+-      /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
+-       * Disable on init and enable only with 100m speed following
+-       * qca original source code.
+-       */
+-      at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
+-                            QCA8327_DEBUG_MANU_CTRL_EN, 0);
+-
+-      return qca83xx_config_init(phydev);
+-}
+-
+-static void qca83xx_link_change_notify(struct phy_device *phydev)
+-{
+-      /* 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_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_ANALOG_TEST_CTRL,
+-                                    QCA8327_DEBUG_MANU_CTRL_EN, 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;
+-
+-      usleep_range(1000, 2000);
+-
+-      return 0;
+-}
+-
+-static int qca83xx_suspend(struct phy_device *phydev)
+-{
+-      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,
+-                            AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
+-                            AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
+-
+-      return 0;
+-}
+-
+-static int qca8337_suspend(struct phy_device *phydev)
+-{
+-      /* Only QCA8337 support actual suspend. */
+-      genphy_suspend(phydev);
+-
+-      return qca83xx_suspend(phydev);
+-}
+-
+-static int qca8327_suspend(struct phy_device *phydev)
+-{
+-      u16 mask = 0;
+-
+-      /* QCA8327 cause port unreliability when phy suspend
+-       * is set.
+-       */
+-      mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
+-      phy_modify(phydev, MII_BMCR, mask, 0);
+-
+-      return qca83xx_suspend(phydev);
+-}
+-
+-static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      /* Enable fast retrain */
+-      ret = genphy_c45_fast_retrain(phydev, true);
+-      if (ret)
+-              return ret;
+-
+-      phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
+-                    QCA808X_TOP_OPTION1_DATA);
+-      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
+-                    QCA808X_MSE_THRESHOLD_20DB_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
+-                    QCA808X_MSE_THRESHOLD_17DB_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
+-                    QCA808X_MSE_THRESHOLD_27DB_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
+-                    QCA808X_MSE_THRESHOLD_28DB_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
+-                    QCA808X_MMD3_DEBUG_1_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
+-                    QCA808X_MMD3_DEBUG_4_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
+-                    QCA808X_MMD3_DEBUG_5_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
+-                    QCA808X_MMD3_DEBUG_3_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
+-                    QCA808X_MMD3_DEBUG_6_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
+-                    QCA808X_MMD3_DEBUG_2_VALUE);
+-
+-      return 0;
+-}
+-
+-static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
+-{
+-      u16 seed_value;
+-
+-      if (!enable)
+-              return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
+-                              QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
+-
+-      seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE);
+-      return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
+-                      QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
+-                      FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
+-                      QCA808X_MASTER_SLAVE_SEED_ENABLE);
+-}
+-
+-static bool qca808x_is_prefer_master(struct phy_device *phydev)
+-{
+-      return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
+-              (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
+-}
+-
+-static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
+-{
+-      return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+-}
+-
+-static int qca808x_config_init(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      /* Active adc&vga on 802.3az for the link 1000M and 100M */
+-      ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
+-                           QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
+-      if (ret)
+-              return ret;
+-
+-      /* Adjust the threshold on 802.3az for the link 1000M */
+-      ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
+-                          QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
+-                          QCA808X_MMD3_AZ_TRAINING_VAL);
+-      if (ret)
+-              return ret;
+-
+-      if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
+-              /* Config the fast retrain for the link 2500M */
+-              ret = qca808x_phy_fast_retrain_config(phydev);
+-              if (ret)
+-                      return ret;
+-
+-              ret = genphy_read_master_slave(phydev);
+-              if (ret < 0)
+-                      return ret;
+-
+-              if (!qca808x_is_prefer_master(phydev)) {
+-                      /* Enable seed and configure lower ramdom seed to make phy
+-                       * linked as slave mode.
+-                       */
+-                      ret = qca808x_phy_ms_seed_enable(phydev, true);
+-                      if (ret)
+-                              return ret;
+-              }
+-      }
+-
+-      /* Configure adc threshold as 100mv for the link 10M */
+-      return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
+-                                   QCA808X_ADC_THRESHOLD_MASK,
+-                                   QCA808X_ADC_THRESHOLD_100MV);
+-}
+-
+-static int qca808x_read_status(struct phy_device *phydev)
+-{
+-      struct at803x_ss_mask ss_mask = { 0 };
+-      int ret;
+-
+-      ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+-      if (ret < 0)
+-              return ret;
+-
+-      linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
+-                       ret & MDIO_AN_10GBT_STAT_LP2_5G);
+-
+-      ret = genphy_read_status(phydev);
+-      if (ret)
+-              return ret;
+-
+-      /* qca8081 takes the different bits for speed value from at803x */
+-      ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
+-      ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
+-      ret = at803x_read_specific_status(phydev, ss_mask);
+-      if (ret < 0)
+-              return ret;
+-
+-      if (phydev->link) {
+-              if (phydev->speed == SPEED_2500)
+-                      phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+-              else
+-                      phydev->interface = PHY_INTERFACE_MODE_SGMII;
+-      } else {
+-              /* generate seed as a lower random value to make PHY linked as SLAVE easily,
+-               * except for master/slave configuration fault detected or the master mode
+-               * preferred.
+-               *
+-               * the reason for not putting this code into the function link_change_notify is
+-               * the corner case where the link partner is also the qca8081 PHY and the seed
+-               * value is configured as the same value, the link can't be up and no link change
+-               * occurs.
+-               */
+-              if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
+-                      if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
+-                          qca808x_is_prefer_master(phydev)) {
+-                              qca808x_phy_ms_seed_enable(phydev, false);
+-                      } else {
+-                              qca808x_phy_ms_seed_enable(phydev, true);
+-                      }
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+-static int qca808x_soft_reset(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      ret = genphy_soft_reset(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      if (qca808x_has_fast_retrain_or_slave_seed(phydev))
+-              ret = qca808x_phy_ms_seed_enable(phydev, true);
+-
+-      return ret;
+-}
+-
+-static bool qca808x_cdt_fault_length_valid(int cdt_code)
+-{
+-      switch (cdt_code) {
+-      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+-              return true;
+-      default:
+-              return false;
+-      }
+-}
+-
+-static int qca808x_cable_test_result_trans(int cdt_code)
+-{
+-      switch (cdt_code) {
+-      case QCA808X_CDT_STATUS_STAT_NORMAL:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+-      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+-      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
+-      case QCA808X_CDT_STATUS_STAT_FAIL:
+-      default:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+-      }
+-}
+-
+-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
+-                                  int result)
+-{
+-      int val;
+-      u32 cdt_length_reg = 0;
+-
+-      switch (pair) {
+-      case ETHTOOL_A_CABLE_PAIR_A:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_B:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_C:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_D:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
+-      if (val < 0)
+-              return val;
+-
+-      if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
+-              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
+-      else
+-              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
+-
+-      return at803x_cdt_fault_length(val);
+-}
+-
+-static int qca808x_cable_test_start(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      /* perform CDT with the following configs:
+-       * 1. disable hibernation.
+-       * 2. force PHY working in MDI mode.
+-       * 3. for PHY working in 1000BaseT.
+-       * 4. configure the threshold.
+-       */
+-
+-      ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = at803x_config_mdix(phydev, ETH_TP_MDI);
+-      if (ret < 0)
+-              return ret;
+-
+-      /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */
+-      phydev->duplex = DUPLEX_FULL;
+-      phydev->speed = SPEED_1000;
+-      ret = genphy_c45_pma_setup_forced(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = genphy_setup_forced(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      /* configure the thresholds for open, short, pair ok test */
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
+-
+-      return 0;
+-}
+-
+-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
+-                                            u16 status)
+-{
+-      int length, result;
+-      u16 pair_code;
+-
+-      switch (pair) {
+-      case ETHTOOL_A_CABLE_PAIR_A:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_B:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_C:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_D:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      result = qca808x_cable_test_result_trans(pair_code);
+-      ethnl_cable_test_result(phydev, pair, result);
+-
+-      if (qca808x_cdt_fault_length_valid(pair_code)) {
+-              length = qca808x_cdt_fault_length(phydev, pair, result);
+-              ethnl_cable_test_fault_length(phydev, pair, length);
+-      }
+-
+-      return 0;
+-}
+-
+-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
+-{
+-      int ret, val;
+-
+-      *finished = false;
+-
+-      val = QCA808X_CDT_ENABLE_TEST |
+-            QCA808X_CDT_LENGTH_UNIT;
+-      ret = at803x_cdt_start(phydev, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
+-      if (ret)
+-              return ret;
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
+-      if (val < 0)
+-              return val;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
+-      if (ret)
+-              return ret;
+-
+-      *finished = true;
+-
+-      return 0;
+-}
+-
+-static int qca808x_get_features(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      ret = genphy_c45_pma_read_abilities(phydev);
+-      if (ret)
+-              return ret;
+-
+-      /* The autoneg ability is not existed in bit3 of MMD7.1,
+-       * but it is supported by qca808x PHY, so we add it here
+-       * manually.
+-       */
+-      linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
+-
+-      /* As for the qca8081 1G version chip, the 2500baseT ability is also
+-       * existed in the bit0 of MMD1.21, we need to remove it manually if
+-       * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
+-       */
+-      ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
+-      if (ret < 0)
+-              return ret;
+-
+-      if (QCA808X_PHY_CHIP_TYPE_1G & ret)
+-              linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+-
+-      return 0;
+-}
+-
+-static int qca808x_config_aneg(struct phy_device *phydev)
+-{
+-      int phy_ctrl = 0;
+-      int ret;
+-
+-      ret = at803x_prepare_config_aneg(phydev);
+-      if (ret)
+-              return ret;
+-
+-      /* The reg MII_BMCR also needs to be configured for force mode, the
+-       * genphy_config_aneg is also needed.
+-       */
+-      if (phydev->autoneg == AUTONEG_DISABLE)
+-              genphy_c45_pma_setup_forced(phydev);
+-
+-      if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
+-              phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
+-
+-      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+-                                   MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
+-      if (ret < 0)
+-              return ret;
+-
+-      return __genphy_config_aneg(phydev, ret);
+-}
+-
+-static void qca808x_link_change_notify(struct phy_device *phydev)
+-{
+-      /* Assert interface sgmii fifo on link down, deassert it on link up,
+-       * the interface device address is always phy address added by 1.
+-       */
+-      mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
+-                                 MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
+-                                 QCA8081_PHY_FIFO_RSTN,
+-                                 phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
+-}
+-
+-static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
+-                                  u16 *offload_trigger)
+-{
+-      /* Parsing specific to netdev trigger */
+-      if (test_bit(TRIGGER_NETDEV_TX, &rules))
+-              *offload_trigger |= QCA808X_LED_TX_BLINK;
+-      if (test_bit(TRIGGER_NETDEV_RX, &rules))
+-              *offload_trigger |= QCA808X_LED_RX_BLINK;
+-      if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
+-              *offload_trigger |= QCA808X_LED_SPEED10_ON;
+-      if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
+-              *offload_trigger |= QCA808X_LED_SPEED100_ON;
+-      if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
+-              *offload_trigger |= QCA808X_LED_SPEED1000_ON;
+-      if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
+-              *offload_trigger |= QCA808X_LED_SPEED2500_ON;
+-      if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
+-              *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
+-      if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
+-              *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
+-
+-      if (rules && !*offload_trigger)
+-              return -EOPNOTSUPP;
+-
+-      /* Enable BLINK_CHECK_BYPASS by default to make the LED
+-       * blink even with duplex or speed mode not enabled.
+-       */
+-      *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
+-
+-      return 0;
+-}
+-
+-static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
+-{
+-      u16 reg;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
+-                                QCA808X_LED_FORCE_EN);
+-}
+-
+-static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
+-                                     unsigned long rules)
+-{
+-      u16 offload_trigger = 0;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
+-}
+-
+-static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
+-                                    unsigned long rules)
+-{
+-      u16 reg, offload_trigger = 0;
+-      int ret;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_CTRL(index);
+-
+-      ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_led_hw_control_enable(phydev, index);
+-      if (ret)
+-              return ret;
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-                            QCA808X_LED_PATTERN_MASK,
+-                            offload_trigger);
+-}
+-
+-static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
+-{
+-      u16 reg;
+-      int val;
+-
+-      if (index > 2)
+-              return false;
+-
+-      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
+-
+-      return !(val & QCA808X_LED_FORCE_EN);
+-}
+-
+-static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
+-                                    unsigned long *rules)
+-{
+-      u16 reg;
+-      int val;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      /* Check if we have hw control enabled */
+-      if (qca808x_led_hw_control_status(phydev, index))
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_CTRL(index);
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
+-      if (val & QCA808X_LED_TX_BLINK)
+-              set_bit(TRIGGER_NETDEV_TX, rules);
+-      if (val & QCA808X_LED_RX_BLINK)
+-              set_bit(TRIGGER_NETDEV_RX, rules);
+-      if (val & QCA808X_LED_SPEED10_ON)
+-              set_bit(TRIGGER_NETDEV_LINK_10, rules);
+-      if (val & QCA808X_LED_SPEED100_ON)
+-              set_bit(TRIGGER_NETDEV_LINK_100, rules);
+-      if (val & QCA808X_LED_SPEED1000_ON)
+-              set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+-      if (val & QCA808X_LED_SPEED2500_ON)
+-              set_bit(TRIGGER_NETDEV_LINK_2500, rules);
+-      if (val & QCA808X_LED_HALF_DUPLEX_ON)
+-              set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
+-      if (val & QCA808X_LED_FULL_DUPLEX_ON)
+-              set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
+-
+-      return 0;
+-}
+-
+-static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
+-{
+-      u16 reg;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_CTRL(index);
+-
+-      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
+-                                QCA808X_LED_PATTERN_MASK);
+-}
+-
+-static int qca808x_led_brightness_set(struct phy_device *phydev,
+-                                    u8 index, enum led_brightness value)
+-{
+-      u16 reg;
+-      int ret;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      if (!value) {
+-              ret = qca808x_led_hw_control_reset(phydev, index);
+-              if (ret)
+-                      return ret;
+-      }
+-
+-      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-                            QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
+-                            QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
+-                                                           QCA808X_LED_FORCE_OFF);
+-}
+-
+-static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
+-                               unsigned long *delay_on,
+-                               unsigned long *delay_off)
+-{
+-      int ret;
+-      u16 reg;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      /* Set blink to 50% off, 50% on at 4Hz by default */
+-      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
+-                           QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
+-                           QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
+-      if (ret)
+-              return ret;
+-
+-      /* We use BLINK_1 for normal blinking */
+-      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
+-                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
+-      if (ret)
+-              return ret;
+-
+-      /* We set blink to 4Hz, aka 250ms */
+-      *delay_on = 250 / 2;
+-      *delay_off = 250 / 2;
+-
+-      return 0;
+-}
+-
+-static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
+-                                  unsigned long modes)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      bool active_low = false;
+-      u32 mode;
+-
+-      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
+-              switch (mode) {
+-              case PHY_LED_ACTIVE_LOW:
+-                      active_low = true;
+-                      break;
+-              default:
+-                      return -EINVAL;
+-              }
+-      }
+-
+-      /* PHY polarity is global and can't be set per LED.
+-       * To detect this, check if last requested polarity mode
+-       * match the new one.
+-       */
+-      if (priv->led_polarity_mode >= 0 &&
+-          priv->led_polarity_mode != active_low) {
+-              phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
+-              return -EINVAL;
+-      }
+-
+-      /* Save the last PHY polarity mode */
+-      priv->led_polarity_mode = active_low;
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_AN,
+-                            QCA808X_MMD7_LED_POLARITY_CTRL,
+-                            QCA808X_LED_ACTIVE_HIGH,
+-                            active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
+-}
+-
+-static struct phy_driver at803x_driver[] = {
+-{
+-      /* Qualcomm Atheros AR8035 */
+-      PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
+-      .name                   = "Qualcomm Atheros AR8035",
+-      .flags                  = PHY_POLL_CABLE_TEST,
+-      .probe                  = at8035_probe,
+-      .config_aneg            = at803x_config_aneg,
+-      .config_init            = at803x_config_init,
+-      .soft_reset             = genphy_soft_reset,
+-      .set_wol                = at803x_set_wol,
+-      .get_wol                = at803x_get_wol,
+-      .suspend                = at803x_suspend,
+-      .resume                 = at803x_resume,
+-      /* PHY_GBIT_FEATURES */
+-      .read_status            = at803x_read_status,
+-      .config_intr            = at803x_config_intr,
+-      .handle_interrupt       = at803x_handle_interrupt,
+-      .get_tunable            = at803x_get_tunable,
+-      .set_tunable            = at803x_set_tunable,
+-      .cable_test_start       = at8031_cable_test_start,
+-      .cable_test_get_status  = at8031_cable_test_get_status,
+-}, {
+-      /* Qualcomm Atheros AR8030 */
+-      .phy_id                 = ATH8030_PHY_ID,
+-      .name                   = "Qualcomm Atheros AR8030",
+-      .phy_id_mask            = AT8030_PHY_ID_MASK,
+-      .probe                  = at8035_probe,
+-      .config_init            = at803x_config_init,
+-      .link_change_notify     = at803x_link_change_notify,
+-      .set_wol                = at803x_set_wol,
+-      .get_wol                = at803x_get_wol,
+-      .suspend                = at803x_suspend,
+-      .resume                 = at803x_resume,
+-      /* PHY_BASIC_FEATURES */
+-      .config_intr            = at803x_config_intr,
+-      .handle_interrupt       = at803x_handle_interrupt,
+-}, {
+-      /* Qualcomm Atheros AR8031/AR8033 */
+-      PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
+-      .name                   = "Qualcomm Atheros AR8031/AR8033",
+-      .flags                  = PHY_POLL_CABLE_TEST,
+-      .probe                  = at8031_probe,
+-      .config_init            = at8031_config_init,
+-      .config_aneg            = at803x_config_aneg,
+-      .soft_reset             = genphy_soft_reset,
+-      .set_wol                = at8031_set_wol,
+-      .get_wol                = at803x_get_wol,
+-      .suspend                = at803x_suspend,
+-      .resume                 = at803x_resume,
+-      .read_page              = at803x_read_page,
+-      .write_page             = at803x_write_page,
+-      .get_features           = at803x_get_features,
+-      .read_status            = at8031_read_status,
+-      .config_intr            = at8031_config_intr,
+-      .handle_interrupt       = at803x_handle_interrupt,
+-      .get_tunable            = at803x_get_tunable,
+-      .set_tunable            = at803x_set_tunable,
+-      .cable_test_start       = at8031_cable_test_start,
+-      .cable_test_get_status  = at8031_cable_test_get_status,
+-}, {
+-      /* Qualcomm Atheros AR8032 */
+-      PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
+-      .name                   = "Qualcomm Atheros AR8032",
+-      .probe                  = at803x_probe,
+-      .flags                  = PHY_POLL_CABLE_TEST,
+-      .config_init            = at803x_config_init,
+-      .link_change_notify     = at803x_link_change_notify,
+-      .suspend                = at803x_suspend,
+-      .resume                 = at803x_resume,
+-      /* PHY_BASIC_FEATURES */
+-      .config_intr            = at803x_config_intr,
+-      .handle_interrupt       = at803x_handle_interrupt,
+-      .cable_test_start       = at803x_cable_test_start,
+-      .cable_test_get_status  = at8032_cable_test_get_status,
+-}, {
+-      /* ATHEROS AR9331 */
+-      PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
+-      .name                   = "Qualcomm Atheros AR9331 built-in PHY",
+-      .probe                  = at803x_probe,
+-      .suspend                = at803x_suspend,
+-      .resume                 = at803x_resume,
+-      .flags                  = PHY_POLL_CABLE_TEST,
+-      /* PHY_BASIC_FEATURES */
+-      .config_intr            = at803x_config_intr,
+-      .handle_interrupt       = at803x_handle_interrupt,
+-      .cable_test_start       = at803x_cable_test_start,
+-      .cable_test_get_status  = at8032_cable_test_get_status,
+-      .read_status            = at803x_read_status,
+-      .soft_reset             = genphy_soft_reset,
+-      .config_aneg            = at803x_config_aneg,
+-}, {
+-      /* Qualcomm Atheros QCA9561 */
+-      PHY_ID_MATCH_EXACT(QCA9561_PHY_ID),
+-      .name                   = "Qualcomm Atheros QCA9561 built-in PHY",
+-      .probe                  = at803x_probe,
+-      .suspend                = at803x_suspend,
+-      .resume                 = at803x_resume,
+-      .flags                  = PHY_POLL_CABLE_TEST,
+-      /* PHY_BASIC_FEATURES */
+-      .config_intr            = at803x_config_intr,
+-      .handle_interrupt       = at803x_handle_interrupt,
+-      .cable_test_start       = at803x_cable_test_start,
+-      .cable_test_get_status  = at8032_cable_test_get_status,
+-      .read_status            = at803x_read_status,
+-      .soft_reset             = genphy_soft_reset,
+-      .config_aneg            = at803x_config_aneg,
+-}, {
+-      /* QCA8337 */
+-      .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         = qca83xx_get_sset_count,
+-      .get_strings            = qca83xx_get_strings,
+-      .get_stats              = qca83xx_get_stats,
+-      .suspend                = qca8337_suspend,
+-      .resume                 = qca83xx_resume,
+-}, {
+-      /* QCA8327-A from switch QCA8327-AL1A */
+-      .phy_id                 = QCA8327_A_PHY_ID,
+-      .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            = qca8327_config_init,
+-      .soft_reset             = genphy_soft_reset,
+-      .get_sset_count         = qca83xx_get_sset_count,
+-      .get_strings            = qca83xx_get_strings,
+-      .get_stats              = qca83xx_get_stats,
+-      .suspend                = qca8327_suspend,
+-      .resume                 = qca83xx_resume,
+-}, {
+-      /* QCA8327-B from switch QCA8327-BL1A */
+-      .phy_id                 = QCA8327_B_PHY_ID,
+-      .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            = qca8327_config_init,
+-      .soft_reset             = genphy_soft_reset,
+-      .get_sset_count         = qca83xx_get_sset_count,
+-      .get_strings            = qca83xx_get_strings,
+-      .get_stats              = qca83xx_get_stats,
+-      .suspend                = qca8327_suspend,
+-      .resume                 = qca83xx_resume,
+-}, {
+-      /* Qualcomm QCA8081 */
+-      PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
+-      .name                   = "Qualcomm QCA8081",
+-      .flags                  = PHY_POLL_CABLE_TEST,
+-      .probe                  = at803x_probe,
+-      .config_intr            = at803x_config_intr,
+-      .handle_interrupt       = at803x_handle_interrupt,
+-      .get_tunable            = at803x_get_tunable,
+-      .set_tunable            = at803x_set_tunable,
+-      .set_wol                = at803x_set_wol,
+-      .get_wol                = at803x_get_wol,
+-      .get_features           = qca808x_get_features,
+-      .config_aneg            = qca808x_config_aneg,
+-      .suspend                = genphy_suspend,
+-      .resume                 = genphy_resume,
+-      .read_status            = qca808x_read_status,
+-      .config_init            = qca808x_config_init,
+-      .soft_reset             = qca808x_soft_reset,
+-      .cable_test_start       = qca808x_cable_test_start,
+-      .cable_test_get_status  = qca808x_cable_test_get_status,
+-      .link_change_notify     = qca808x_link_change_notify,
+-      .led_brightness_set     = qca808x_led_brightness_set,
+-      .led_blink_set          = qca808x_led_blink_set,
+-      .led_hw_is_supported    = qca808x_led_hw_is_supported,
+-      .led_hw_control_set     = qca808x_led_hw_control_set,
+-      .led_hw_control_get     = qca808x_led_hw_control_get,
+-      .led_polarity_set       = qca808x_led_polarity_set,
+-}, };
+-
+-module_phy_driver(at803x_driver);
+-
+-static struct mdio_device_id __maybe_unused atheros_tbl[] = {
+-      { ATH8030_PHY_ID, AT8030_PHY_ID_MASK },
+-      { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) },
+-      { 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_A_PHY_ID) },
+-      { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
+-      { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
+-      { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
+-      { }
+-};
+-
+-MODULE_DEVICE_TABLE(mdio, atheros_tbl);
+--- /dev/null
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -0,0 +1,2759 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * drivers/net/phy/at803x.c
++ *
++ * Driver for Qualcomm Atheros AR803x PHY
++ *
++ * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
++ */
++
++#include <linux/phy.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/ethtool_netlink.h>
++#include <linux/bitfield.h>
++#include <linux/regulator/of_regulator.h>
++#include <linux/regulator/driver.h>
++#include <linux/regulator/consumer.h>
++#include <linux/of.h>
++#include <linux/phylink.h>
++#include <linux/sfp.h>
++#include <dt-bindings/net/qca-ar803x.h>
++
++#define AT803X_SPECIFIC_FUNCTION_CONTROL      0x10
++#define AT803X_SFC_ASSERT_CRS                 BIT(11)
++#define AT803X_SFC_FORCE_LINK                 BIT(10)
++#define AT803X_SFC_MDI_CROSSOVER_MODE_M               GENMASK(6, 5)
++#define AT803X_SFC_AUTOMATIC_CROSSOVER                0x3
++#define AT803X_SFC_MANUAL_MDIX                        0x1
++#define AT803X_SFC_MANUAL_MDI                 0x0
++#define AT803X_SFC_SQE_TEST                   BIT(2)
++#define AT803X_SFC_POLARITY_REVERSAL          BIT(1)
++#define AT803X_SFC_DISABLE_JABBER             BIT(0)
++
++#define AT803X_SPECIFIC_STATUS                        0x11
++#define AT803X_SS_SPEED_MASK                  GENMASK(15, 14)
++#define AT803X_SS_SPEED_1000                  2
++#define AT803X_SS_SPEED_100                   1
++#define AT803X_SS_SPEED_10                    0
++#define AT803X_SS_DUPLEX                      BIT(13)
++#define AT803X_SS_SPEED_DUPLEX_RESOLVED               BIT(11)
++#define AT803X_SS_MDIX                                BIT(6)
++
++#define QCA808X_SS_SPEED_MASK                 GENMASK(9, 7)
++#define QCA808X_SS_SPEED_2500                 4
++
++#define AT803X_INTR_ENABLE                    0x12
++#define AT803X_INTR_ENABLE_AUTONEG_ERR                BIT(15)
++#define AT803X_INTR_ENABLE_SPEED_CHANGED      BIT(14)
++#define AT803X_INTR_ENABLE_DUPLEX_CHANGED     BIT(13)
++#define AT803X_INTR_ENABLE_PAGE_RECEIVED      BIT(12)
++#define AT803X_INTR_ENABLE_LINK_FAIL          BIT(11)
++#define AT803X_INTR_ENABLE_LINK_SUCCESS               BIT(10)
++#define AT803X_INTR_ENABLE_LINK_FAIL_BX               BIT(8)
++#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX    BIT(7)
++#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE        BIT(5)
++#define AT803X_INTR_ENABLE_POLARITY_CHANGED   BIT(1)
++#define AT803X_INTR_ENABLE_WOL                        BIT(0)
++
++#define AT803X_INTR_STATUS                    0x13
++
++#define AT803X_SMART_SPEED                    0x14
++#define AT803X_SMART_SPEED_ENABLE             BIT(5)
++#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK   GENMASK(4, 2)
++#define AT803X_SMART_SPEED_BYPASS_TIMER               BIT(1)
++#define AT803X_CDT                            0x16
++#define AT803X_CDT_MDI_PAIR_MASK              GENMASK(9, 8)
++#define AT803X_CDT_ENABLE_TEST                        BIT(0)
++#define AT803X_CDT_STATUS                     0x1c
++#define AT803X_CDT_STATUS_STAT_NORMAL         0
++#define AT803X_CDT_STATUS_STAT_SHORT          1
++#define AT803X_CDT_STATUS_STAT_OPEN           2
++#define AT803X_CDT_STATUS_STAT_FAIL           3
++#define AT803X_CDT_STATUS_STAT_MASK           GENMASK(9, 8)
++#define AT803X_CDT_STATUS_DELTA_TIME_MASK     GENMASK(7, 0)
++#define AT803X_LED_CONTROL                    0x18
++
++#define AT803X_PHY_MMD3_WOL_CTRL              0x8012
++#define AT803X_WOL_EN                         BIT(5)
++#define AT803X_LOC_MAC_ADDR_0_15_OFFSET               0x804C
++#define AT803X_LOC_MAC_ADDR_16_31_OFFSET      0x804B
++#define AT803X_LOC_MAC_ADDR_32_47_OFFSET      0x804A
++#define AT803X_REG_CHIP_CONFIG                        0x1f
++#define AT803X_BT_BX_REG_SEL                  0x8000
++
++#define AT803X_DEBUG_ADDR                     0x1D
++#define AT803X_DEBUG_DATA                     0x1E
++
++#define AT803X_MODE_CFG_MASK                  0x0F
++#define AT803X_MODE_CFG_BASET_RGMII           0x00
++#define AT803X_MODE_CFG_BASET_SGMII           0x01
++#define AT803X_MODE_CFG_BX1000_RGMII_50OHM    0x02
++#define AT803X_MODE_CFG_BX1000_RGMII_75OHM    0x03
++#define AT803X_MODE_CFG_BX1000_CONV_50OHM     0x04
++#define AT803X_MODE_CFG_BX1000_CONV_75OHM     0x05
++#define AT803X_MODE_CFG_FX100_RGMII_50OHM     0x06
++#define AT803X_MODE_CFG_FX100_CONV_50OHM      0x07
++#define AT803X_MODE_CFG_RGMII_AUTO_MDET               0x0B
++#define AT803X_MODE_CFG_FX100_RGMII_75OHM     0x0E
++#define AT803X_MODE_CFG_FX100_CONV_75OHM      0x0F
++
++#define AT803X_PSSR                           0x11    /*PHY-Specific Status Register*/
++#define AT803X_PSSR_MR_AN_COMPLETE            0x0200
++
++#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_SYSTEM_CTRL_MODE         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_HIB_CTRL_PS_HIB_EN     BIT(15)
++
++#define AT803X_DEBUG_REG_3C                   0x3C
++
++#define AT803X_DEBUG_REG_GREEN                        0x3D
++#define   AT803X_DEBUG_GATE_CLK_IN1000                BIT(6)
++
++#define AT803X_DEBUG_REG_1F                   0x1F
++#define AT803X_DEBUG_PLL_ON                   BIT(2)
++#define AT803X_DEBUG_RGMII_1V8                        BIT(3)
++
++#define MDIO_AZ_DEBUG                         0x800D
++
++/* AT803x supports either the XTAL input pad, an internal PLL or the
++ * DSP as clock reference for the clock output pad. The XTAL reference
++ * is only used for 25 MHz output, all other frequencies need the PLL.
++ * The DSP as a clock reference is used in synchronous ethernet
++ * applications.
++ *
++ * By default the PLL is only enabled if there is a link. Otherwise
++ * the PHY will go into low power state and disabled the PLL. You can
++ * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always
++ * enabled.
++ */
++#define AT803X_MMD7_CLK25M                    0x8016
++#define AT803X_CLK_OUT_MASK                   GENMASK(4, 2)
++#define AT803X_CLK_OUT_25MHZ_XTAL             0
++#define AT803X_CLK_OUT_25MHZ_DSP              1
++#define AT803X_CLK_OUT_50MHZ_PLL              2
++#define AT803X_CLK_OUT_50MHZ_DSP              3
++#define AT803X_CLK_OUT_62_5MHZ_PLL            4
++#define AT803X_CLK_OUT_62_5MHZ_DSP            5
++#define AT803X_CLK_OUT_125MHZ_PLL             6
++#define AT803X_CLK_OUT_125MHZ_DSP             7
++
++/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask
++ * but doesn't support choosing between XTAL/PLL and DSP.
++ */
++#define AT8035_CLK_OUT_MASK                   GENMASK(4, 3)
++
++#define AT803X_CLK_OUT_STRENGTH_MASK          GENMASK(8, 7)
++#define AT803X_CLK_OUT_STRENGTH_FULL          0
++#define AT803X_CLK_OUT_STRENGTH_HALF          1
++#define AT803X_CLK_OUT_STRENGTH_QUARTER               2
++
++#define AT803X_DEFAULT_DOWNSHIFT              5
++#define AT803X_MIN_DOWNSHIFT                  2
++#define AT803X_MAX_DOWNSHIFT                  9
++
++#define AT803X_MMD3_SMARTEEE_CTL1             0x805b
++#define AT803X_MMD3_SMARTEEE_CTL2             0x805c
++#define AT803X_MMD3_SMARTEEE_CTL3             0x805d
++#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN      BIT(8)
++
++#define ATH9331_PHY_ID                                0x004dd041
++#define ATH8030_PHY_ID                                0x004dd076
++#define ATH8031_PHY_ID                                0x004dd074
++#define ATH8032_PHY_ID                                0x004dd023
++#define ATH8035_PHY_ID                                0x004dd072
++#define AT8030_PHY_ID_MASK                    0xffffffef
++
++#define QCA8081_PHY_ID                                0x004dd101
++
++#define QCA8327_A_PHY_ID                      0x004dd033
++#define QCA8327_B_PHY_ID                      0x004dd034
++#define QCA8337_PHY_ID                                0x004dd036
++#define QCA9561_PHY_ID                                0x004dd042
++#define QCA8K_PHY_ID_MASK                     0xffffffff
++
++#define QCA8K_DEVFLAGS_REVISION_MASK          GENMASK(2, 0)
++
++#define AT803X_PAGE_FIBER                     0
++#define AT803X_PAGE_COPPER                    1
++
++/* don't turn off internal PLL */
++#define AT803X_KEEP_PLL_ENABLED                       BIT(0)
++#define AT803X_DISABLE_SMARTEEE                       BIT(1)
++
++/* disable hibernation mode */
++#define AT803X_DISABLE_HIBERNATION_MODE               BIT(2)
++
++/* ADC threshold */
++#define QCA808X_PHY_DEBUG_ADC_THRESHOLD               0x2c80
++#define QCA808X_ADC_THRESHOLD_MASK            GENMASK(7, 0)
++#define QCA808X_ADC_THRESHOLD_80MV            0
++#define QCA808X_ADC_THRESHOLD_100MV           0xf0
++#define QCA808X_ADC_THRESHOLD_200MV           0x0f
++#define QCA808X_ADC_THRESHOLD_300MV           0xff
++
++/* CLD control */
++#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7               0x8007
++#define QCA808X_8023AZ_AFE_CTRL_MASK          GENMASK(8, 4)
++#define QCA808X_8023AZ_AFE_EN                 0x90
++
++/* AZ control */
++#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL     0x8008
++#define QCA808X_MMD3_AZ_TRAINING_VAL          0x1c32
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB   0x8014
++#define QCA808X_MSE_THRESHOLD_20DB_VALUE      0x529
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB   0x800E
++#define QCA808X_MSE_THRESHOLD_17DB_VALUE      0x341
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB   0x801E
++#define QCA808X_MSE_THRESHOLD_27DB_VALUE      0x419
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB   0x8020
++#define QCA808X_MSE_THRESHOLD_28DB_VALUE      0x341
++
++#define QCA808X_PHY_MMD7_TOP_OPTION1          0x901c
++#define QCA808X_TOP_OPTION1_DATA              0x0
++
++#define QCA808X_PHY_MMD3_DEBUG_1              0xa100
++#define QCA808X_MMD3_DEBUG_1_VALUE            0x9203
++#define QCA808X_PHY_MMD3_DEBUG_2              0xa101
++#define QCA808X_MMD3_DEBUG_2_VALUE            0x48ad
++#define QCA808X_PHY_MMD3_DEBUG_3              0xa103
++#define QCA808X_MMD3_DEBUG_3_VALUE            0x1698
++#define QCA808X_PHY_MMD3_DEBUG_4              0xa105
++#define QCA808X_MMD3_DEBUG_4_VALUE            0x8001
++#define QCA808X_PHY_MMD3_DEBUG_5              0xa106
++#define QCA808X_MMD3_DEBUG_5_VALUE            0x1111
++#define QCA808X_PHY_MMD3_DEBUG_6              0xa011
++#define QCA808X_MMD3_DEBUG_6_VALUE            0x5f85
++
++/* master/slave seed config */
++#define QCA808X_PHY_DEBUG_LOCAL_SEED          9
++#define QCA808X_MASTER_SLAVE_SEED_ENABLE      BIT(1)
++#define QCA808X_MASTER_SLAVE_SEED_CFG         GENMASK(12, 2)
++#define QCA808X_MASTER_SLAVE_SEED_RANGE               0x32
++
++/* Hibernation yields lower power consumpiton in contrast with normal operation mode.
++ * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.
++ */
++#define QCA808X_DBG_AN_TEST                   0xb
++#define QCA808X_HIBERNATION_EN                        BIT(15)
++
++#define QCA808X_CDT_ENABLE_TEST                       BIT(15)
++#define QCA808X_CDT_INTER_CHECK_DIS           BIT(13)
++#define QCA808X_CDT_STATUS                    BIT(11)
++#define QCA808X_CDT_LENGTH_UNIT                       BIT(10)
++
++#define QCA808X_MMD3_CDT_STATUS                       0x8064
++#define QCA808X_MMD3_CDT_DIAG_PAIR_A          0x8065
++#define QCA808X_MMD3_CDT_DIAG_PAIR_B          0x8066
++#define QCA808X_MMD3_CDT_DIAG_PAIR_C          0x8067
++#define QCA808X_MMD3_CDT_DIAG_PAIR_D          0x8068
++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT    GENMASK(15, 8)
++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT   GENMASK(7, 0)
++
++#define QCA808X_CDT_CODE_PAIR_A                       GENMASK(15, 12)
++#define QCA808X_CDT_CODE_PAIR_B                       GENMASK(11, 8)
++#define QCA808X_CDT_CODE_PAIR_C                       GENMASK(7, 4)
++#define QCA808X_CDT_CODE_PAIR_D                       GENMASK(3, 0)
++
++#define QCA808X_CDT_STATUS_STAT_TYPE          GENMASK(1, 0)
++#define QCA808X_CDT_STATUS_STAT_FAIL          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
++#define QCA808X_CDT_STATUS_STAT_NORMAL                FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN     FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT    FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
++
++#define QCA808X_CDT_STATUS_STAT_MDI           GENMASK(3, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI1          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
++#define QCA808X_CDT_STATUS_STAT_MDI2          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI3          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
++
++/* NORMAL are MDI with type set to 0 */
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI1
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI2
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI3
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
++
++/* Added for reference of existence but should be handled by wait_for_completion already */
++#define QCA808X_CDT_STATUS_STAT_BUSY          (BIT(1) | BIT(3))
++
++#define QCA808X_MMD7_LED_GLOBAL                       0x8073
++#define QCA808X_LED_BLINK_1                   GENMASK(11, 6)
++#define QCA808X_LED_BLINK_2                   GENMASK(5, 0)
++/* Values are the same for both BLINK_1 and BLINK_2 */
++#define QCA808X_LED_BLINK_FREQ_MASK           GENMASK(5, 3)
++#define QCA808X_LED_BLINK_FREQ_2HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
++#define QCA808X_LED_BLINK_FREQ_4HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
++#define QCA808X_LED_BLINK_FREQ_8HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
++#define QCA808X_LED_BLINK_FREQ_16HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
++#define QCA808X_LED_BLINK_FREQ_32HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
++#define QCA808X_LED_BLINK_FREQ_64HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
++#define QCA808X_LED_BLINK_FREQ_128HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
++#define QCA808X_LED_BLINK_FREQ_256HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
++#define QCA808X_LED_BLINK_DUTY_MASK           GENMASK(2, 0)
++#define QCA808X_LED_BLINK_DUTY_50_50          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
++#define QCA808X_LED_BLINK_DUTY_75_25          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
++#define QCA808X_LED_BLINK_DUTY_25_75          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
++#define QCA808X_LED_BLINK_DUTY_33_67          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
++#define QCA808X_LED_BLINK_DUTY_67_33          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
++#define QCA808X_LED_BLINK_DUTY_17_83          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
++#define QCA808X_LED_BLINK_DUTY_83_17          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
++#define QCA808X_LED_BLINK_DUTY_8_92           FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
++
++#define QCA808X_MMD7_LED2_CTRL                        0x8074
++#define QCA808X_MMD7_LED2_FORCE_CTRL          0x8075
++#define QCA808X_MMD7_LED1_CTRL                        0x8076
++#define QCA808X_MMD7_LED1_FORCE_CTRL          0x8077
++#define QCA808X_MMD7_LED0_CTRL                        0x8078
++#define QCA808X_MMD7_LED_CTRL(x)              (0x8078 - ((x) * 2))
++
++/* LED hw control pattern is the same for every LED */
++#define QCA808X_LED_PATTERN_MASK              GENMASK(15, 0)
++#define QCA808X_LED_SPEED2500_ON              BIT(15)
++#define QCA808X_LED_SPEED2500_BLINK           BIT(14)
++/* Follow blink trigger even if duplex or speed condition doesn't match */
++#define QCA808X_LED_BLINK_CHECK_BYPASS                BIT(13)
++#define QCA808X_LED_FULL_DUPLEX_ON            BIT(12)
++#define QCA808X_LED_HALF_DUPLEX_ON            BIT(11)
++#define QCA808X_LED_TX_BLINK                  BIT(10)
++#define QCA808X_LED_RX_BLINK                  BIT(9)
++#define QCA808X_LED_TX_ON_10MS                        BIT(8)
++#define QCA808X_LED_RX_ON_10MS                        BIT(7)
++#define QCA808X_LED_SPEED1000_ON              BIT(6)
++#define QCA808X_LED_SPEED100_ON                       BIT(5)
++#define QCA808X_LED_SPEED10_ON                        BIT(4)
++#define QCA808X_LED_COLLISION_BLINK           BIT(3)
++#define QCA808X_LED_SPEED1000_BLINK           BIT(2)
++#define QCA808X_LED_SPEED100_BLINK            BIT(1)
++#define QCA808X_LED_SPEED10_BLINK             BIT(0)
++
++#define QCA808X_MMD7_LED0_FORCE_CTRL          0x8079
++#define QCA808X_MMD7_LED_FORCE_CTRL(x)                (0x8079 - ((x) * 2))
++
++/* LED force ctrl is the same for every LED
++ * No documentation exist for this, not even internal one
++ * with NDA as QCOM gives only info about configuring
++ * hw control pattern rules and doesn't indicate any way
++ * to force the LED to specific mode.
++ * These define comes from reverse and testing and maybe
++ * lack of some info or some info are not entirely correct.
++ * For the basic LED control and hw control these finding
++ * are enough to support LED control in all the required APIs.
++ *
++ * On doing some comparison with implementation with qca807x,
++ * it was found that it's 1:1 equal to it and confirms all the
++ * reverse done. It was also found further specification with the
++ * force mode and the blink modes.
++ */
++#define QCA808X_LED_FORCE_EN                  BIT(15)
++#define QCA808X_LED_FORCE_MODE_MASK           GENMASK(14, 13)
++#define QCA808X_LED_FORCE_BLINK_1             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
++#define QCA808X_LED_FORCE_BLINK_2             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
++#define QCA808X_LED_FORCE_ON                  FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
++#define QCA808X_LED_FORCE_OFF                 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
++
++#define QCA808X_MMD7_LED_POLARITY_CTRL                0x901a
++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
++ * LED to active high. It's not clear what BIT 3 and BIT 4 does.
++ */
++#define QCA808X_LED_ACTIVE_HIGH                       BIT(6)
++
++/* QCA808X 1G chip type */
++#define QCA808X_PHY_MMD7_CHIP_TYPE            0x901d
++#define QCA808X_PHY_CHIP_TYPE_1G              BIT(0)
++
++#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL     0x9072
++#define QCA8081_PHY_FIFO_RSTN                 BIT(11)
++
++MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
++MODULE_AUTHOR("Matus Ujhelyi");
++MODULE_LICENSE("GPL");
++
++enum stat_access_type {
++      PHY,
++      MMD
++};
++
++struct at803x_hw_stat {
++      const char *string;
++      u8 reg;
++      u32 mask;
++      enum stat_access_type access_type;
++};
++
++static struct at803x_hw_stat qca83xx_hw_stats[] = {
++      { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
++      { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
++      { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
++};
++
++struct at803x_ss_mask {
++      u16 speed_mask;
++      u8 speed_shift;
++};
++
++struct at803x_priv {
++      int flags;
++      u16 clk_25m_reg;
++      u16 clk_25m_mask;
++      u8 smarteee_lpi_tw_1g;
++      u8 smarteee_lpi_tw_100m;
++      bool is_fiber;
++      bool is_1000basex;
++      struct regulator_dev *vddio_rdev;
++      struct regulator_dev *vddh_rdev;
++      u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
++      int led_polarity_mode;
++};
++
++struct at803x_context {
++      u16 bmcr;
++      u16 advertise;
++      u16 control1000;
++      u16 int_enable;
++      u16 smart_speed;
++      u16 led_control;
++};
++
++static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
++{
++      int ret;
++
++      ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
++      if (ret < 0)
++              return ret;
++
++      return phy_write(phydev, AT803X_DEBUG_DATA, data);
++}
++
++static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
++{
++      int ret;
++
++      ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
++      if (ret < 0)
++              return ret;
++
++      return phy_read(phydev, AT803X_DEBUG_DATA);
++}
++
++static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
++                               u16 clear, u16 set)
++{
++      u16 val;
++      int ret;
++
++      ret = at803x_debug_reg_read(phydev, reg);
++      if (ret < 0)
++              return ret;
++
++      val = ret & 0xffff;
++      val &= ~clear;
++      val |= set;
++
++      return phy_write(phydev, AT803X_DEBUG_DATA, val);
++}
++
++static int at803x_write_page(struct phy_device *phydev, int page)
++{
++      int mask;
++      int set;
++
++      if (page == AT803X_PAGE_COPPER) {
++              set = AT803X_BT_BX_REG_SEL;
++              mask = 0;
++      } else {
++              set = 0;
++              mask = AT803X_BT_BX_REG_SEL;
++      }
++
++      return __phy_modify(phydev, AT803X_REG_CHIP_CONFIG, mask, set);
++}
++
++static int at803x_read_page(struct phy_device *phydev)
++{
++      int ccr = __phy_read(phydev, AT803X_REG_CHIP_CONFIG);
++
++      if (ccr < 0)
++              return ccr;
++
++      if (ccr & AT803X_BT_BX_REG_SEL)
++              return AT803X_PAGE_COPPER;
++
++      return AT803X_PAGE_FIBER;
++}
++
++static int at803x_enable_rx_delay(struct phy_device *phydev)
++{
++      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_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_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_SYSTEM_CTRL_MODE,
++                                   AT803X_DEBUG_TX_CLK_DLY_EN, 0);
++}
++
++/* save relevant PHY registers to private copy */
++static void at803x_context_save(struct phy_device *phydev,
++                              struct at803x_context *context)
++{
++      context->bmcr = phy_read(phydev, MII_BMCR);
++      context->advertise = phy_read(phydev, MII_ADVERTISE);
++      context->control1000 = phy_read(phydev, MII_CTRL1000);
++      context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
++      context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
++      context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
++}
++
++/* restore relevant PHY registers from private copy */
++static void at803x_context_restore(struct phy_device *phydev,
++                                 const struct at803x_context *context)
++{
++      phy_write(phydev, MII_BMCR, context->bmcr);
++      phy_write(phydev, MII_ADVERTISE, context->advertise);
++      phy_write(phydev, MII_CTRL1000, context->control1000);
++      phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
++      phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
++      phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
++}
++
++static int at803x_set_wol(struct phy_device *phydev,
++                        struct ethtool_wolinfo *wol)
++{
++      int ret, irq_enabled;
++
++      if (wol->wolopts & WAKE_MAGIC) {
++              struct net_device *ndev = phydev->attached_dev;
++              const u8 *mac;
++              unsigned int i;
++              static const unsigned int offsets[] = {
++                      AT803X_LOC_MAC_ADDR_32_47_OFFSET,
++                      AT803X_LOC_MAC_ADDR_16_31_OFFSET,
++                      AT803X_LOC_MAC_ADDR_0_15_OFFSET,
++              };
++
++              if (!ndev)
++                      return -ENODEV;
++
++              mac = (const u8 *)ndev->dev_addr;
++
++              if (!is_valid_ether_addr(mac))
++                      return -EINVAL;
++
++              for (i = 0; i < 3; i++)
++                      phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
++                                    mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
++
++              /* Enable WOL interrupt */
++              ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
++              if (ret)
++                      return ret;
++      } else {
++              /* Disable WOL interrupt */
++              ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
++              if (ret)
++                      return ret;
++      }
++
++      /* Clear WOL status */
++      ret = phy_read(phydev, AT803X_INTR_STATUS);
++      if (ret < 0)
++              return ret;
++
++      /* Check if there are other interrupts except for WOL triggered when PHY is
++       * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
++       * be passed up to the interrupt PIN.
++       */
++      irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
++      if (irq_enabled < 0)
++              return irq_enabled;
++
++      irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
++      if (ret & irq_enabled && !phy_polling_mode(phydev))
++              phy_trigger_machine(phydev);
++
++      return 0;
++}
++
++static void at803x_get_wol(struct phy_device *phydev,
++                         struct ethtool_wolinfo *wol)
++{
++      int value;
++
++      wol->supported = WAKE_MAGIC;
++      wol->wolopts = 0;
++
++      value = phy_read(phydev, AT803X_INTR_ENABLE);
++      if (value < 0)
++              return;
++
++      if (value & AT803X_INTR_ENABLE_WOL)
++              wol->wolopts |= WAKE_MAGIC;
++}
++
++static int qca83xx_get_sset_count(struct phy_device *phydev)
++{
++      return ARRAY_SIZE(qca83xx_hw_stats);
++}
++
++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
++              strscpy(data + i * ETH_GSTRING_LEN,
++                      qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
++      }
++}
++
++static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
++{
++      struct at803x_hw_stat stat = qca83xx_hw_stats[i];
++      struct at803x_priv *priv = phydev->priv;
++      int val;
++      u64 ret;
++
++      if (stat.access_type == MMD)
++              val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
++      else
++              val = phy_read(phydev, stat.reg);
++
++      if (val < 0) {
++              ret = U64_MAX;
++      } else {
++              val = val & stat.mask;
++              priv->stats[i] += val;
++              ret = priv->stats[i];
++      }
++
++      return ret;
++}
++
++static void qca83xx_get_stats(struct phy_device *phydev,
++                            struct ethtool_stats *stats, u64 *data)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
++              data[i] = qca83xx_get_stat(phydev, i);
++}
++
++static int at803x_suspend(struct phy_device *phydev)
++{
++      int value;
++      int wol_enabled;
++
++      value = phy_read(phydev, AT803X_INTR_ENABLE);
++      wol_enabled = value & AT803X_INTR_ENABLE_WOL;
++
++      if (wol_enabled)
++              value = BMCR_ISOLATE;
++      else
++              value = BMCR_PDOWN;
++
++      phy_modify(phydev, MII_BMCR, 0, value);
++
++      return 0;
++}
++
++static int at803x_resume(struct phy_device *phydev)
++{
++      return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
++}
++
++static int at803x_parse_dt(struct phy_device *phydev)
++{
++      struct device_node *node = phydev->mdio.dev.of_node;
++      struct at803x_priv *priv = phydev->priv;
++      u32 freq, strength, tw;
++      unsigned int sel;
++      int ret;
++
++      if (!IS_ENABLED(CONFIG_OF_MDIO))
++              return 0;
++
++      if (of_property_read_bool(node, "qca,disable-smarteee"))
++              priv->flags |= AT803X_DISABLE_SMARTEEE;
++
++      if (of_property_read_bool(node, "qca,disable-hibernation-mode"))
++              priv->flags |= AT803X_DISABLE_HIBERNATION_MODE;
++
++      if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) {
++              if (!tw || tw > 255) {
++                      phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n");
++                      return -EINVAL;
++              }
++              priv->smarteee_lpi_tw_1g = tw;
++      }
++
++      if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) {
++              if (!tw || tw > 255) {
++                      phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n");
++                      return -EINVAL;
++              }
++              priv->smarteee_lpi_tw_100m = tw;
++      }
++
++      ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq);
++      if (!ret) {
++              switch (freq) {
++              case 25000000:
++                      sel = AT803X_CLK_OUT_25MHZ_XTAL;
++                      break;
++              case 50000000:
++                      sel = AT803X_CLK_OUT_50MHZ_PLL;
++                      break;
++              case 62500000:
++                      sel = AT803X_CLK_OUT_62_5MHZ_PLL;
++                      break;
++              case 125000000:
++                      sel = AT803X_CLK_OUT_125MHZ_PLL;
++                      break;
++              default:
++                      phydev_err(phydev, "invalid qca,clk-out-frequency\n");
++                      return -EINVAL;
++              }
++
++              priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
++              priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
++      }
++
++      ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
++      if (!ret) {
++              priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK;
++              switch (strength) {
++              case AR803X_STRENGTH_FULL:
++                      priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL;
++                      break;
++              case AR803X_STRENGTH_HALF:
++                      priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF;
++                      break;
++              case AR803X_STRENGTH_QUARTER:
++                      priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER;
++                      break;
++              default:
++                      phydev_err(phydev, "invalid qca,clk-out-strength\n");
++                      return -EINVAL;
++              }
++      }
++
++      return 0;
++}
++
++static int at803x_probe(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      struct at803x_priv *priv;
++      int ret;
++
++      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      /* Init LED polarity mode to -1 */
++      priv->led_polarity_mode = -1;
++
++      phydev->priv = priv;
++
++      ret = at803x_parse_dt(phydev);
++      if (ret)
++              return ret;
++
++      return 0;
++}
++
++static int at803x_get_features(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      int err;
++
++      err = genphy_read_abilities(phydev);
++      if (err)
++              return err;
++
++      if (phydev->drv->phy_id != ATH8031_PHY_ID)
++              return 0;
++
++      /* AR8031/AR8033 have different status registers
++       * for copper and fiber operation. However, the
++       * extended status register is the same for both
++       * operation modes.
++       *
++       * As a result of that, ESTATUS_1000_XFULL is set
++       * to 1 even when operating in copper TP mode.
++       *
++       * Remove this mode from the supported link modes
++       * when not operating in 1000BaseX mode.
++       */
++      if (!priv->is_1000basex)
++              linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
++                                 phydev->supported);
++
++      return 0;
++}
++
++static int at803x_smarteee_config(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      u16 mask = 0, val = 0;
++      int ret;
++
++      if (priv->flags & AT803X_DISABLE_SMARTEEE)
++              return phy_modify_mmd(phydev, MDIO_MMD_PCS,
++                                    AT803X_MMD3_SMARTEEE_CTL3,
++                                    AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0);
++
++      if (priv->smarteee_lpi_tw_1g) {
++              mask |= 0xff00;
++              val |= priv->smarteee_lpi_tw_1g << 8;
++      }
++      if (priv->smarteee_lpi_tw_100m) {
++              mask |= 0x00ff;
++              val |= priv->smarteee_lpi_tw_100m;
++      }
++      if (!mask)
++              return 0;
++
++      ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1,
++                           mask, val);
++      if (ret)
++              return ret;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3,
++                            AT803X_MMD3_SMARTEEE_CTL3_LPI_EN,
++                            AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
++}
++
++static int at803x_clk_out_config(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++
++      if (!priv->clk_25m_mask)
++              return 0;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M,
++                            priv->clk_25m_mask, priv->clk_25m_reg);
++}
++
++static int at8031_pll_config(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++
++      /* The default after hardware reset is PLL OFF. After a soft reset, the
++       * values are retained.
++       */
++      if (priv->flags & AT803X_KEEP_PLL_ENABLED)
++              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
++                                           0, AT803X_DEBUG_PLL_ON);
++      else
++              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
++                                           AT803X_DEBUG_PLL_ON, 0);
++}
++
++static int at803x_hibernation_mode_config(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++
++      /* The default after hardware reset is hibernation mode enabled. After
++       * software reset, the value is retained.
++       */
++      if (!(priv->flags & AT803X_DISABLE_HIBERNATION_MODE))
++              return 0;
++
++      return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
++                                       AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0);
++}
++
++static int at803x_config_init(struct phy_device *phydev)
++{
++      int ret;
++
++      /* The RX and TX delay default is:
++       *   after HW reset: RX delay enabled and TX delay disabled
++       *   after SW reset: RX delay enabled, while TX delay retains the
++       *   value before reset.
++       */
++      if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
++          phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
++              ret = at803x_enable_rx_delay(phydev);
++      else
++              ret = at803x_disable_rx_delay(phydev);
++      if (ret < 0)
++              return ret;
++
++      if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
++          phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
++              ret = at803x_enable_tx_delay(phydev);
++      else
++              ret = at803x_disable_tx_delay(phydev);
++      if (ret < 0)
++              return ret;
++
++      ret = at803x_smarteee_config(phydev);
++      if (ret < 0)
++              return ret;
++
++      ret = at803x_clk_out_config(phydev);
++      if (ret < 0)
++              return ret;
++
++      ret = at803x_hibernation_mode_config(phydev);
++      if (ret < 0)
++              return ret;
++
++      /* Ar803x extended next page bit is enabled by default. Cisco
++       * multigig switches read this bit and attempt to negotiate 10Gbps
++       * rates even if the next page bit is disabled. This is incorrect
++       * behaviour but we still need to accommodate it. XNP is only needed
++       * for 10Gbps support, so disable XNP.
++       */
++      return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
++}
++
++static int at803x_ack_interrupt(struct phy_device *phydev)
++{
++      int err;
++
++      err = phy_read(phydev, AT803X_INTR_STATUS);
++
++      return (err < 0) ? err : 0;
++}
++
++static int at803x_config_intr(struct phy_device *phydev)
++{
++      int err;
++      int value;
++
++      value = phy_read(phydev, AT803X_INTR_ENABLE);
++
++      if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
++              /* Clear any pending interrupts */
++              err = at803x_ack_interrupt(phydev);
++              if (err)
++                      return err;
++
++              value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
++              value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
++              value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
++              value |= AT803X_INTR_ENABLE_LINK_FAIL;
++              value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
++
++              err = phy_write(phydev, AT803X_INTR_ENABLE, value);
++      } else {
++              err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
++              if (err)
++                      return err;
++
++              /* Clear any pending interrupts */
++              err = at803x_ack_interrupt(phydev);
++      }
++
++      return err;
++}
++
++static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
++{
++      int irq_status, int_enabled;
++
++      irq_status = phy_read(phydev, AT803X_INTR_STATUS);
++      if (irq_status < 0) {
++              phy_error(phydev);
++              return IRQ_NONE;
++      }
++
++      /* Read the current enabled interrupts */
++      int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
++      if (int_enabled < 0) {
++              phy_error(phydev);
++              return IRQ_NONE;
++      }
++
++      /* See if this was one of our enabled interrupts */
++      if (!(irq_status & int_enabled))
++              return IRQ_NONE;
++
++      phy_trigger_machine(phydev);
++
++      return IRQ_HANDLED;
++}
++
++static void at803x_link_change_notify(struct phy_device *phydev)
++{
++      /*
++       * Conduct a hardware reset for AT8030 every time a link loss is
++       * signalled. This is necessary to circumvent a hardware bug that
++       * occurs when the cable is unplugged while TX packets are pending
++       * in the FIFO. In such cases, the FIFO enters an error mode it
++       * cannot recover from by software.
++       */
++      if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) {
++              struct at803x_context context;
++
++              at803x_context_save(phydev, &context);
++
++              phy_device_reset(phydev, 1);
++              usleep_range(1000, 2000);
++              phy_device_reset(phydev, 0);
++              usleep_range(1000, 2000);
++
++              at803x_context_restore(phydev, &context);
++
++              phydev_dbg(phydev, "%s(): phy was reset\n", __func__);
++      }
++}
++
++static int at803x_read_specific_status(struct phy_device *phydev,
++                                     struct at803x_ss_mask ss_mask)
++{
++      int ss;
++
++      /* Read the AT8035 PHY-Specific Status register, which indicates the
++       * speed and duplex that the PHY is actually using, irrespective of
++       * whether we are in autoneg mode or not.
++       */
++      ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
++      if (ss < 0)
++              return ss;
++
++      if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
++              int sfc, speed;
++
++              sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
++              if (sfc < 0)
++                      return sfc;
++
++              speed = ss & ss_mask.speed_mask;
++              speed >>= ss_mask.speed_shift;
++
++              switch (speed) {
++              case AT803X_SS_SPEED_10:
++                      phydev->speed = SPEED_10;
++                      break;
++              case AT803X_SS_SPEED_100:
++                      phydev->speed = SPEED_100;
++                      break;
++              case AT803X_SS_SPEED_1000:
++                      phydev->speed = SPEED_1000;
++                      break;
++              case QCA808X_SS_SPEED_2500:
++                      phydev->speed = SPEED_2500;
++                      break;
++              }
++              if (ss & AT803X_SS_DUPLEX)
++                      phydev->duplex = DUPLEX_FULL;
++              else
++                      phydev->duplex = DUPLEX_HALF;
++
++              if (ss & AT803X_SS_MDIX)
++                      phydev->mdix = ETH_TP_MDI_X;
++              else
++                      phydev->mdix = ETH_TP_MDI;
++
++              switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
++              case AT803X_SFC_MANUAL_MDI:
++                      phydev->mdix_ctrl = ETH_TP_MDI;
++                      break;
++              case AT803X_SFC_MANUAL_MDIX:
++                      phydev->mdix_ctrl = ETH_TP_MDI_X;
++                      break;
++              case AT803X_SFC_AUTOMATIC_CROSSOVER:
++                      phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
++                      break;
++              }
++      }
++
++      return 0;
++}
++
++static int at803x_read_status(struct phy_device *phydev)
++{
++      struct at803x_ss_mask ss_mask = { 0 };
++      int err, old_link = phydev->link;
++
++      /* Update the link, but return if there was an error */
++      err = genphy_update_link(phydev);
++      if (err)
++              return err;
++
++      /* why bother the PHY if nothing can have changed */
++      if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
++              return 0;
++
++      phydev->speed = SPEED_UNKNOWN;
++      phydev->duplex = DUPLEX_UNKNOWN;
++      phydev->pause = 0;
++      phydev->asym_pause = 0;
++
++      err = genphy_read_lpa(phydev);
++      if (err < 0)
++              return err;
++
++      ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
++      ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
++      err = at803x_read_specific_status(phydev, ss_mask);
++      if (err < 0)
++              return err;
++
++      if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
++              phy_resolve_aneg_pause(phydev);
++
++      return 0;
++}
++
++static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
++{
++      u16 val;
++
++      switch (ctrl) {
++      case ETH_TP_MDI:
++              val = AT803X_SFC_MANUAL_MDI;
++              break;
++      case ETH_TP_MDI_X:
++              val = AT803X_SFC_MANUAL_MDIX;
++              break;
++      case ETH_TP_MDI_AUTO:
++              val = AT803X_SFC_AUTOMATIC_CROSSOVER;
++              break;
++      default:
++              return 0;
++      }
++
++      return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
++                        AT803X_SFC_MDI_CROSSOVER_MODE_M,
++                        FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
++}
++
++static int at803x_prepare_config_aneg(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
++      if (ret < 0)
++              return ret;
++
++      /* Changes of the midx bits are disruptive to the normal operation;
++       * therefore any changes to these registers must be followed by a
++       * software reset to take effect.
++       */
++      if (ret == 1) {
++              ret = genphy_soft_reset(phydev);
++              if (ret < 0)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static int at803x_config_aneg(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      int ret;
++
++      ret = at803x_prepare_config_aneg(phydev);
++      if (ret)
++              return ret;
++
++      if (priv->is_1000basex)
++              return genphy_c37_config_aneg(phydev);
++
++      return genphy_config_aneg(phydev);
++}
++
++static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
++{
++      int val;
++
++      val = phy_read(phydev, AT803X_SMART_SPEED);
++      if (val < 0)
++              return val;
++
++      if (val & AT803X_SMART_SPEED_ENABLE)
++              *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
++      else
++              *d = DOWNSHIFT_DEV_DISABLE;
++
++      return 0;
++}
++
++static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
++{
++      u16 mask, set;
++      int ret;
++
++      switch (cnt) {
++      case DOWNSHIFT_DEV_DEFAULT_COUNT:
++              cnt = AT803X_DEFAULT_DOWNSHIFT;
++              fallthrough;
++      case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
++              set = AT803X_SMART_SPEED_ENABLE |
++                    AT803X_SMART_SPEED_BYPASS_TIMER |
++                    FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
++              mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
++              break;
++      case DOWNSHIFT_DEV_DISABLE:
++              set = 0;
++              mask = AT803X_SMART_SPEED_ENABLE |
++                     AT803X_SMART_SPEED_BYPASS_TIMER;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
++
++      /* After changing the smart speed settings, we need to perform a
++       * software reset, use phy_init_hw() to make sure we set the
++       * reapply any values which might got lost during software reset.
++       */
++      if (ret == 1)
++              ret = phy_init_hw(phydev);
++
++      return ret;
++}
++
++static int at803x_get_tunable(struct phy_device *phydev,
++                            struct ethtool_tunable *tuna, void *data)
++{
++      switch (tuna->id) {
++      case ETHTOOL_PHY_DOWNSHIFT:
++              return at803x_get_downshift(phydev, data);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++static int at803x_set_tunable(struct phy_device *phydev,
++                            struct ethtool_tunable *tuna, const void *data)
++{
++      switch (tuna->id) {
++      case ETHTOOL_PHY_DOWNSHIFT:
++              return at803x_set_downshift(phydev, *(const u8 *)data);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++static int at803x_cable_test_result_trans(u16 status)
++{
++      switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
++      case AT803X_CDT_STATUS_STAT_NORMAL:
++              return ETHTOOL_A_CABLE_RESULT_CODE_OK;
++      case AT803X_CDT_STATUS_STAT_SHORT:
++              return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
++      case AT803X_CDT_STATUS_STAT_OPEN:
++              return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
++      case AT803X_CDT_STATUS_STAT_FAIL:
++      default:
++              return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
++      }
++}
++
++static bool at803x_cdt_test_failed(u16 status)
++{
++      return FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status) ==
++              AT803X_CDT_STATUS_STAT_FAIL;
++}
++
++static bool at803x_cdt_fault_length_valid(u16 status)
++{
++      switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
++      case AT803X_CDT_STATUS_STAT_OPEN:
++      case AT803X_CDT_STATUS_STAT_SHORT:
++              return true;
++      }
++      return false;
++}
++
++static int at803x_cdt_fault_length(int dt)
++{
++      /* According to the datasheet the distance to the fault is
++       * DELTA_TIME * 0.824 meters.
++       *
++       * The author suspect the correct formula is:
++       *
++       *   fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
++       *
++       * where c is the speed of light, VF is the velocity factor of
++       * the twisted pair cable, 125MHz the counter frequency and
++       * we need to divide by 2 because the hardware will measure the
++       * round trip time to the fault and back to the PHY.
++       *
++       * With a VF of 0.69 we get the factor 0.824 mentioned in the
++       * datasheet.
++       */
++      return (dt * 824) / 10;
++}
++
++static int at803x_cdt_start(struct phy_device *phydev,
++                          u32 cdt_start)
++{
++      return phy_write(phydev, AT803X_CDT, cdt_start);
++}
++
++static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
++                                        u32 cdt_en)
++{
++      int val, ret;
++
++      /* One test run takes about 25ms */
++      ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
++                                  !(val & cdt_en),
++                                  30000, 100000, true);
++
++      return ret < 0 ? ret : 0;
++}
++
++static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair)
++{
++      static const int ethtool_pair[] = {
++              ETHTOOL_A_CABLE_PAIR_A,
++              ETHTOOL_A_CABLE_PAIR_B,
++              ETHTOOL_A_CABLE_PAIR_C,
++              ETHTOOL_A_CABLE_PAIR_D,
++      };
++      int ret, val;
++
++      val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
++            AT803X_CDT_ENABLE_TEST;
++      ret = at803x_cdt_start(phydev, val);
++      if (ret)
++              return ret;
++
++      ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST);
++      if (ret)
++              return ret;
++
++      val = phy_read(phydev, AT803X_CDT_STATUS);
++      if (val < 0)
++              return val;
++
++      if (at803x_cdt_test_failed(val))
++              return 0;
++
++      ethnl_cable_test_result(phydev, ethtool_pair[pair],
++                              at803x_cable_test_result_trans(val));
++
++      if (at803x_cdt_fault_length_valid(val)) {
++              val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val);
++              ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
++                                            at803x_cdt_fault_length(val));
++      }
++
++      return 1;
++}
++
++static int at803x_cable_test_get_status(struct phy_device *phydev,
++                                      bool *finished, unsigned long pair_mask)
++{
++      int retries = 20;
++      int pair, ret;
++
++      *finished = false;
++
++      /* According to the datasheet the CDT can be performed when
++       * there is no link partner or when the link partner is
++       * auto-negotiating. Starting the test will restart the AN
++       * automatically. It seems that doing this repeatedly we will
++       * get a slot where our link partner won't disturb our
++       * measurement.
++       */
++      while (pair_mask && retries--) {
++              for_each_set_bit(pair, &pair_mask, 4) {
++                      ret = at803x_cable_test_one_pair(phydev, pair);
++                      if (ret < 0)
++                              return ret;
++                      if (ret)
++                              clear_bit(pair, &pair_mask);
++              }
++              if (pair_mask)
++                      msleep(250);
++      }
++
++      *finished = true;
++
++      return 0;
++}
++
++static void at803x_cable_test_autoneg(struct phy_device *phydev)
++{
++      /* Enable auto-negotiation, but advertise no capabilities, no link
++       * will be established. A restart of the auto-negotiation is not
++       * required, because the cable test will automatically break the link.
++       */
++      phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
++      phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
++}
++
++static int at803x_cable_test_start(struct phy_device *phydev)
++{
++      at803x_cable_test_autoneg(phydev);
++      /* we do all the (time consuming) work later */
++      return 0;
++}
++
++static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
++                                          unsigned int selector)
++{
++      struct phy_device *phydev = rdev_get_drvdata(rdev);
++
++      if (selector)
++              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
++                                           0, AT803X_DEBUG_RGMII_1V8);
++      else
++              return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
++                                           AT803X_DEBUG_RGMII_1V8, 0);
++}
++
++static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
++{
++      struct phy_device *phydev = rdev_get_drvdata(rdev);
++      int val;
++
++      val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
++      if (val < 0)
++              return val;
++
++      return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
++}
++
++static const struct regulator_ops vddio_regulator_ops = {
++      .list_voltage = regulator_list_voltage_table,
++      .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
++      .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
++};
++
++static const unsigned int vddio_voltage_table[] = {
++      1500000,
++      1800000,
++};
++
++static const struct regulator_desc vddio_desc = {
++      .name = "vddio",
++      .of_match = of_match_ptr("vddio-regulator"),
++      .n_voltages = ARRAY_SIZE(vddio_voltage_table),
++      .volt_table = vddio_voltage_table,
++      .ops = &vddio_regulator_ops,
++      .type = REGULATOR_VOLTAGE,
++      .owner = THIS_MODULE,
++};
++
++static const struct regulator_ops vddh_regulator_ops = {
++};
++
++static const struct regulator_desc vddh_desc = {
++      .name = "vddh",
++      .of_match = of_match_ptr("vddh-regulator"),
++      .n_voltages = 1,
++      .fixed_uV = 2500000,
++      .ops = &vddh_regulator_ops,
++      .type = REGULATOR_VOLTAGE,
++      .owner = THIS_MODULE,
++};
++
++static int at8031_register_regulators(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      struct device *dev = &phydev->mdio.dev;
++      struct regulator_config config = { };
++
++      config.dev = dev;
++      config.driver_data = phydev;
++
++      priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
++      if (IS_ERR(priv->vddio_rdev)) {
++              phydev_err(phydev, "failed to register VDDIO regulator\n");
++              return PTR_ERR(priv->vddio_rdev);
++      }
++
++      priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
++      if (IS_ERR(priv->vddh_rdev)) {
++              phydev_err(phydev, "failed to register VDDH regulator\n");
++              return PTR_ERR(priv->vddh_rdev);
++      }
++
++      return 0;
++}
++
++static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++{
++      struct phy_device *phydev = upstream;
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
++      DECLARE_PHY_INTERFACE_MASK(interfaces);
++      phy_interface_t iface;
++
++      linkmode_zero(phy_support);
++      phylink_set(phy_support, 1000baseX_Full);
++      phylink_set(phy_support, 1000baseT_Full);
++      phylink_set(phy_support, Autoneg);
++      phylink_set(phy_support, Pause);
++      phylink_set(phy_support, Asym_Pause);
++
++      linkmode_zero(sfp_support);
++      sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
++      /* Some modules support 10G modes as well as others we support.
++       * Mask out non-supported modes so the correct interface is picked.
++       */
++      linkmode_and(sfp_support, phy_support, sfp_support);
++
++      if (linkmode_empty(sfp_support)) {
++              dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
++              return -EINVAL;
++      }
++
++      iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
++
++      /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
++       * interface for use with SFP modules.
++       * However, some copper modules detected as having a preferred SGMII
++       * interface do default to and function in 1000Base-X mode, so just
++       * print a warning and allow such modules, as they may have some chance
++       * of working.
++       */
++      if (iface == PHY_INTERFACE_MODE_SGMII)
++              dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
++      else if (iface != PHY_INTERFACE_MODE_1000BASEX)
++              return -EINVAL;
++
++      return 0;
++}
++
++static const struct sfp_upstream_ops at8031_sfp_ops = {
++      .attach = phy_sfp_attach,
++      .detach = phy_sfp_detach,
++      .module_insert = at8031_sfp_insert,
++};
++
++static int at8031_parse_dt(struct phy_device *phydev)
++{
++      struct device_node *node = phydev->mdio.dev.of_node;
++      struct at803x_priv *priv = phydev->priv;
++      int ret;
++
++      if (of_property_read_bool(node, "qca,keep-pll-enabled"))
++              priv->flags |= AT803X_KEEP_PLL_ENABLED;
++
++      ret = at8031_register_regulators(phydev);
++      if (ret < 0)
++              return ret;
++
++      ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
++                                               "vddio");
++      if (ret) {
++              phydev_err(phydev, "failed to get VDDIO regulator\n");
++              return ret;
++      }
++
++      /* Only AR8031/8033 support 1000Base-X for SFP modules */
++      return phy_sfp_probe(phydev, &at8031_sfp_ops);
++}
++
++static int at8031_probe(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      int mode_cfg;
++      int ccr;
++      int ret;
++
++      ret = at803x_probe(phydev);
++      if (ret)
++              return ret;
++
++      /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
++       * options.
++       */
++      ret = at8031_parse_dt(phydev);
++      if (ret)
++              return ret;
++
++      ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
++      if (ccr < 0)
++              return ccr;
++      mode_cfg = ccr & AT803X_MODE_CFG_MASK;
++
++      switch (mode_cfg) {
++      case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
++      case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
++              priv->is_1000basex = true;
++              fallthrough;
++      case AT803X_MODE_CFG_FX100_RGMII_50OHM:
++      case AT803X_MODE_CFG_FX100_RGMII_75OHM:
++              priv->is_fiber = true;
++              break;
++      }
++
++      /* Disable WoL in 1588 register which is enabled
++       * by default
++       */
++      return phy_modify_mmd(phydev, MDIO_MMD_PCS,
++                            AT803X_PHY_MMD3_WOL_CTRL,
++                            AT803X_WOL_EN, 0);
++}
++
++static int at8031_config_init(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      int ret;
++
++      /* Some bootloaders leave the fiber page selected.
++       * Switch to the appropriate page (fiber or copper), as otherwise we
++       * read the PHY capabilities from the wrong page.
++       */
++      phy_lock_mdio_bus(phydev);
++      ret = at803x_write_page(phydev,
++                              priv->is_fiber ? AT803X_PAGE_FIBER :
++                                               AT803X_PAGE_COPPER);
++      phy_unlock_mdio_bus(phydev);
++      if (ret)
++              return ret;
++
++      ret = at8031_pll_config(phydev);
++      if (ret < 0)
++              return ret;
++
++      return at803x_config_init(phydev);
++}
++
++static int at8031_set_wol(struct phy_device *phydev,
++                        struct ethtool_wolinfo *wol)
++{
++      int ret;
++
++      /* First setup MAC address and enable WOL interrupt */
++      ret = at803x_set_wol(phydev, wol);
++      if (ret)
++              return ret;
++
++      if (wol->wolopts & WAKE_MAGIC)
++              /* Enable WOL function for 1588 */
++              ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
++                                   AT803X_PHY_MMD3_WOL_CTRL,
++                                   0, AT803X_WOL_EN);
++      else
++              /* Disable WoL function for 1588 */
++              ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
++                                   AT803X_PHY_MMD3_WOL_CTRL,
++                                   AT803X_WOL_EN, 0);
++
++      return ret;
++}
++
++static int at8031_config_intr(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++      int err, value = 0;
++
++      if (phydev->interrupts == PHY_INTERRUPT_ENABLED &&
++          priv->is_fiber) {
++              /* Clear any pending interrupts */
++              err = at803x_ack_interrupt(phydev);
++              if (err)
++                      return err;
++
++              value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
++              value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
++
++              err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value);
++              if (err)
++                      return err;
++      }
++
++      return at803x_config_intr(phydev);
++}
++
++/* AR8031 and AR8033 share the same read status logic */
++static int at8031_read_status(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++
++      if (priv->is_1000basex)
++              return genphy_c37_read_status(phydev);
++
++      return at803x_read_status(phydev);
++}
++
++/* AR8031 and AR8035 share the same cable test get status reg */
++static int at8031_cable_test_get_status(struct phy_device *phydev,
++                                      bool *finished)
++{
++      return at803x_cable_test_get_status(phydev, finished, 0xf);
++}
++
++/* AR8031 and AR8035 share the same cable test start logic */
++static int at8031_cable_test_start(struct phy_device *phydev)
++{
++      at803x_cable_test_autoneg(phydev);
++      phy_write(phydev, MII_CTRL1000, 0);
++      /* we do all the (time consuming) work later */
++      return 0;
++}
++
++/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */
++static int at8032_cable_test_get_status(struct phy_device *phydev,
++                                      bool *finished)
++{
++      return at803x_cable_test_get_status(phydev, finished, 0x3);
++}
++
++static int at8035_parse_dt(struct phy_device *phydev)
++{
++      struct at803x_priv *priv = phydev->priv;
++
++      /* Mask is set by the generic at803x_parse_dt
++       * if property is set. Assume property is set
++       * with the mask not zero.
++       */
++      if (priv->clk_25m_mask) {
++              /* Fixup for the AR8030/AR8035. This chip has another mask and
++               * doesn't support the DSP reference. Eg. the lowest bit of the
++               * mask. The upper two bits select the same frequencies. Mask
++               * the lowest bit here.
++               *
++               * Warning:
++               *   There was no datasheet for the AR8030 available so this is
++               *   just a guess. But the AR8035 is listed as pin compatible
++               *   to the AR8030 so there might be a good chance it works on
++               *   the AR8030 too.
++               */
++              priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
++              priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
++      }
++
++      return 0;
++}
++
++/* AR8030 and AR8035 shared the same special mask for clk_25m */
++static int at8035_probe(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = at803x_probe(phydev);
++      if (ret)
++              return ret;
++
++      return at8035_parse_dt(phydev);
++}
++
++static int qca83xx_config_init(struct phy_device *phydev)
++{
++      u8 switch_revision;
++
++      switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
++
++      switch (switch_revision) {
++      case 1:
++              /* For 100M waveform */
++              at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
++              /* Turn on Gigabit clock */
++              at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
++              break;
++
++      case 2:
++              phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
++              fallthrough;
++      case 4:
++              phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
++              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;
++      }
++
++      /* Following original QCA sourcecode set port to prefer master */
++      phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
++
++      return 0;
++}
++
++static int qca8327_config_init(struct phy_device *phydev)
++{
++      /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
++       * Disable on init and enable only with 100m speed following
++       * qca original source code.
++       */
++      at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
++                            QCA8327_DEBUG_MANU_CTRL_EN, 0);
++
++      return qca83xx_config_init(phydev);
++}
++
++static void qca83xx_link_change_notify(struct phy_device *phydev)
++{
++      /* 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_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_ANALOG_TEST_CTRL,
++                                    QCA8327_DEBUG_MANU_CTRL_EN, 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;
++
++      usleep_range(1000, 2000);
++
++      return 0;
++}
++
++static int qca83xx_suspend(struct phy_device *phydev)
++{
++      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,
++                            AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
++                            AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
++
++      return 0;
++}
++
++static int qca8337_suspend(struct phy_device *phydev)
++{
++      /* Only QCA8337 support actual suspend. */
++      genphy_suspend(phydev);
++
++      return qca83xx_suspend(phydev);
++}
++
++static int qca8327_suspend(struct phy_device *phydev)
++{
++      u16 mask = 0;
++
++      /* QCA8327 cause port unreliability when phy suspend
++       * is set.
++       */
++      mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
++      phy_modify(phydev, MII_BMCR, mask, 0);
++
++      return qca83xx_suspend(phydev);
++}
++
++static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
++{
++      int ret;
++
++      /* Enable fast retrain */
++      ret = genphy_c45_fast_retrain(phydev, true);
++      if (ret)
++              return ret;
++
++      phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
++                    QCA808X_TOP_OPTION1_DATA);
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
++                    QCA808X_MSE_THRESHOLD_20DB_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
++                    QCA808X_MSE_THRESHOLD_17DB_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
++                    QCA808X_MSE_THRESHOLD_27DB_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
++                    QCA808X_MSE_THRESHOLD_28DB_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
++                    QCA808X_MMD3_DEBUG_1_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
++                    QCA808X_MMD3_DEBUG_4_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
++                    QCA808X_MMD3_DEBUG_5_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
++                    QCA808X_MMD3_DEBUG_3_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
++                    QCA808X_MMD3_DEBUG_6_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
++                    QCA808X_MMD3_DEBUG_2_VALUE);
++
++      return 0;
++}
++
++static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
++{
++      u16 seed_value;
++
++      if (!enable)
++              return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
++                              QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
++
++      seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE);
++      return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
++                      QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
++                      FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
++                      QCA808X_MASTER_SLAVE_SEED_ENABLE);
++}
++
++static bool qca808x_is_prefer_master(struct phy_device *phydev)
++{
++      return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
++              (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
++}
++
++static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
++{
++      return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
++}
++
++static int qca808x_config_init(struct phy_device *phydev)
++{
++      int ret;
++
++      /* Active adc&vga on 802.3az for the link 1000M and 100M */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
++                           QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
++      if (ret)
++              return ret;
++
++      /* Adjust the threshold on 802.3az for the link 1000M */
++      ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
++                          QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
++                          QCA808X_MMD3_AZ_TRAINING_VAL);
++      if (ret)
++              return ret;
++
++      if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
++              /* Config the fast retrain for the link 2500M */
++              ret = qca808x_phy_fast_retrain_config(phydev);
++              if (ret)
++                      return ret;
++
++              ret = genphy_read_master_slave(phydev);
++              if (ret < 0)
++                      return ret;
++
++              if (!qca808x_is_prefer_master(phydev)) {
++                      /* Enable seed and configure lower ramdom seed to make phy
++                       * linked as slave mode.
++                       */
++                      ret = qca808x_phy_ms_seed_enable(phydev, true);
++                      if (ret)
++                              return ret;
++              }
++      }
++
++      /* Configure adc threshold as 100mv for the link 10M */
++      return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
++                                   QCA808X_ADC_THRESHOLD_MASK,
++                                   QCA808X_ADC_THRESHOLD_100MV);
++}
++
++static int qca808x_read_status(struct phy_device *phydev)
++{
++      struct at803x_ss_mask ss_mask = { 0 };
++      int ret;
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
++      if (ret < 0)
++              return ret;
++
++      linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
++                       ret & MDIO_AN_10GBT_STAT_LP2_5G);
++
++      ret = genphy_read_status(phydev);
++      if (ret)
++              return ret;
++
++      /* qca8081 takes the different bits for speed value from at803x */
++      ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
++      ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
++      ret = at803x_read_specific_status(phydev, ss_mask);
++      if (ret < 0)
++              return ret;
++
++      if (phydev->link) {
++              if (phydev->speed == SPEED_2500)
++                      phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
++              else
++                      phydev->interface = PHY_INTERFACE_MODE_SGMII;
++      } else {
++              /* generate seed as a lower random value to make PHY linked as SLAVE easily,
++               * except for master/slave configuration fault detected or the master mode
++               * preferred.
++               *
++               * the reason for not putting this code into the function link_change_notify is
++               * the corner case where the link partner is also the qca8081 PHY and the seed
++               * value is configured as the same value, the link can't be up and no link change
++               * occurs.
++               */
++              if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
++                      if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
++                          qca808x_is_prefer_master(phydev)) {
++                              qca808x_phy_ms_seed_enable(phydev, false);
++                      } else {
++                              qca808x_phy_ms_seed_enable(phydev, true);
++                      }
++              }
++      }
++
++      return 0;
++}
++
++static int qca808x_soft_reset(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = genphy_soft_reset(phydev);
++      if (ret < 0)
++              return ret;
++
++      if (qca808x_has_fast_retrain_or_slave_seed(phydev))
++              ret = qca808x_phy_ms_seed_enable(phydev, true);
++
++      return ret;
++}
++
++static bool qca808x_cdt_fault_length_valid(int cdt_code)
++{
++      switch (cdt_code) {
++      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++              return true;
++      default:
++              return false;
++      }
++}
++
++static int qca808x_cable_test_result_trans(int cdt_code)
++{
++      switch (cdt_code) {
++      case QCA808X_CDT_STATUS_STAT_NORMAL:
++              return ETHTOOL_A_CABLE_RESULT_CODE_OK;
++      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++              return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
++      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++              return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++              return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
++      case QCA808X_CDT_STATUS_STAT_FAIL:
++      default:
++              return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
++      }
++}
++
++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
++                                  int result)
++{
++      int val;
++      u32 cdt_length_reg = 0;
++
++      switch (pair) {
++      case ETHTOOL_A_CABLE_PAIR_A:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
++              break;
++      case ETHTOOL_A_CABLE_PAIR_B:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
++              break;
++      case ETHTOOL_A_CABLE_PAIR_C:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
++              break;
++      case ETHTOOL_A_CABLE_PAIR_D:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
++      if (val < 0)
++              return val;
++
++      if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
++              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
++      else
++              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
++
++      return at803x_cdt_fault_length(val);
++}
++
++static int qca808x_cable_test_start(struct phy_device *phydev)
++{
++      int ret;
++
++      /* perform CDT with the following configs:
++       * 1. disable hibernation.
++       * 2. force PHY working in MDI mode.
++       * 3. for PHY working in 1000BaseT.
++       * 4. configure the threshold.
++       */
++
++      ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);
++      if (ret < 0)
++              return ret;
++
++      ret = at803x_config_mdix(phydev, ETH_TP_MDI);
++      if (ret < 0)
++              return ret;
++
++      /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */
++      phydev->duplex = DUPLEX_FULL;
++      phydev->speed = SPEED_1000;
++      ret = genphy_c45_pma_setup_forced(phydev);
++      if (ret < 0)
++              return ret;
++
++      ret = genphy_setup_forced(phydev);
++      if (ret < 0)
++              return ret;
++
++      /* configure the thresholds for open, short, pair ok test */
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
++
++      return 0;
++}
++
++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
++                                            u16 status)
++{
++      int length, result;
++      u16 pair_code;
++
++      switch (pair) {
++      case ETHTOOL_A_CABLE_PAIR_A:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_B:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_C:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_D:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      result = qca808x_cable_test_result_trans(pair_code);
++      ethnl_cable_test_result(phydev, pair, result);
++
++      if (qca808x_cdt_fault_length_valid(pair_code)) {
++              length = qca808x_cdt_fault_length(phydev, pair, result);
++              ethnl_cable_test_fault_length(phydev, pair, length);
++      }
++
++      return 0;
++}
++
++static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
++{
++      int ret, val;
++
++      *finished = false;
++
++      val = QCA808X_CDT_ENABLE_TEST |
++            QCA808X_CDT_LENGTH_UNIT;
++      ret = at803x_cdt_start(phydev, val);
++      if (ret)
++              return ret;
++
++      ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
++      if (ret)
++              return ret;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
++      if (val < 0)
++              return val;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
++      if (ret)
++              return ret;
++
++      *finished = true;
++
++      return 0;
++}
++
++static int qca808x_get_features(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = genphy_c45_pma_read_abilities(phydev);
++      if (ret)
++              return ret;
++
++      /* The autoneg ability is not existed in bit3 of MMD7.1,
++       * but it is supported by qca808x PHY, so we add it here
++       * manually.
++       */
++      linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
++
++      /* As for the qca8081 1G version chip, the 2500baseT ability is also
++       * existed in the bit0 of MMD1.21, we need to remove it manually if
++       * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
++       */
++      ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
++      if (ret < 0)
++              return ret;
++
++      if (QCA808X_PHY_CHIP_TYPE_1G & ret)
++              linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
++
++      return 0;
++}
++
++static int qca808x_config_aneg(struct phy_device *phydev)
++{
++      int phy_ctrl = 0;
++      int ret;
++
++      ret = at803x_prepare_config_aneg(phydev);
++      if (ret)
++              return ret;
++
++      /* The reg MII_BMCR also needs to be configured for force mode, the
++       * genphy_config_aneg is also needed.
++       */
++      if (phydev->autoneg == AUTONEG_DISABLE)
++              genphy_c45_pma_setup_forced(phydev);
++
++      if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
++              phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
++
++      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
++                                   MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
++      if (ret < 0)
++              return ret;
++
++      return __genphy_config_aneg(phydev, ret);
++}
++
++static void qca808x_link_change_notify(struct phy_device *phydev)
++{
++      /* Assert interface sgmii fifo on link down, deassert it on link up,
++       * the interface device address is always phy address added by 1.
++       */
++      mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
++                                 MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
++                                 QCA8081_PHY_FIFO_RSTN,
++                                 phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
++}
++
++static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
++                                  u16 *offload_trigger)
++{
++      /* Parsing specific to netdev trigger */
++      if (test_bit(TRIGGER_NETDEV_TX, &rules))
++              *offload_trigger |= QCA808X_LED_TX_BLINK;
++      if (test_bit(TRIGGER_NETDEV_RX, &rules))
++              *offload_trigger |= QCA808X_LED_RX_BLINK;
++      if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED10_ON;
++      if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED100_ON;
++      if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED1000_ON;
++      if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED2500_ON;
++      if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
++              *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
++      if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
++              *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
++
++      if (rules && !*offload_trigger)
++              return -EOPNOTSUPP;
++
++      /* Enable BLINK_CHECK_BYPASS by default to make the LED
++       * blink even with duplex or speed mode not enabled.
++       */
++      *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
++
++      return 0;
++}
++
++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++                                QCA808X_LED_FORCE_EN);
++}
++
++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                     unsigned long rules)
++{
++      u16 offload_trigger = 0;
++
++      if (index > 2)
++              return -EINVAL;
++
++      return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
++}
++
++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                    unsigned long rules)
++{
++      u16 reg, offload_trigger = 0;
++      int ret;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_CTRL(index);
++
++      ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
++      if (ret)
++              return ret;
++
++      ret = qca808x_led_hw_control_enable(phydev, index);
++      if (ret)
++              return ret;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                            QCA808X_LED_PATTERN_MASK,
++                            offload_trigger);
++}
++
++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++      int val;
++
++      if (index > 2)
++              return false;
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++
++      return !(val & QCA808X_LED_FORCE_EN);
++}
++
++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                    unsigned long *rules)
++{
++      u16 reg;
++      int val;
++
++      if (index > 2)
++              return -EINVAL;
++
++      /* Check if we have hw control enabled */
++      if (qca808x_led_hw_control_status(phydev, index))
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_CTRL(index);
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++      if (val & QCA808X_LED_TX_BLINK)
++              set_bit(TRIGGER_NETDEV_TX, rules);
++      if (val & QCA808X_LED_RX_BLINK)
++              set_bit(TRIGGER_NETDEV_RX, rules);
++      if (val & QCA808X_LED_SPEED10_ON)
++              set_bit(TRIGGER_NETDEV_LINK_10, rules);
++      if (val & QCA808X_LED_SPEED100_ON)
++              set_bit(TRIGGER_NETDEV_LINK_100, rules);
++      if (val & QCA808X_LED_SPEED1000_ON)
++              set_bit(TRIGGER_NETDEV_LINK_1000, rules);
++      if (val & QCA808X_LED_SPEED2500_ON)
++              set_bit(TRIGGER_NETDEV_LINK_2500, rules);
++      if (val & QCA808X_LED_HALF_DUPLEX_ON)
++              set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
++      if (val & QCA808X_LED_FULL_DUPLEX_ON)
++              set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
++
++      return 0;
++}
++
++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_CTRL(index);
++
++      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++                                QCA808X_LED_PATTERN_MASK);
++}
++
++static int qca808x_led_brightness_set(struct phy_device *phydev,
++                                    u8 index, enum led_brightness value)
++{
++      u16 reg;
++      int ret;
++
++      if (index > 2)
++              return -EINVAL;
++
++      if (!value) {
++              ret = qca808x_led_hw_control_reset(phydev, index);
++              if (ret)
++                      return ret;
++      }
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                            QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++                            QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
++                                                           QCA808X_LED_FORCE_OFF);
++}
++
++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
++                               unsigned long *delay_on,
++                               unsigned long *delay_off)
++{
++      int ret;
++      u16 reg;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      /* Set blink to 50% off, 50% on at 4Hz by default */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
++                           QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
++                           QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
++      if (ret)
++              return ret;
++
++      /* We use BLINK_1 for normal blinking */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
++      if (ret)
++              return ret;
++
++      /* We set blink to 4Hz, aka 250ms */
++      *delay_on = 250 / 2;
++      *delay_off = 250 / 2;
++
++      return 0;
++}
++
++static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
++                                  unsigned long modes)
++{
++      struct at803x_priv *priv = phydev->priv;
++      bool active_low = false;
++      u32 mode;
++
++      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++              switch (mode) {
++              case PHY_LED_ACTIVE_LOW:
++                      active_low = true;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      /* PHY polarity is global and can't be set per LED.
++       * To detect this, check if last requested polarity mode
++       * match the new one.
++       */
++      if (priv->led_polarity_mode >= 0 &&
++          priv->led_polarity_mode != active_low) {
++              phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
++              return -EINVAL;
++      }
++
++      /* Save the last PHY polarity mode */
++      priv->led_polarity_mode = active_low;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN,
++                            QCA808X_MMD7_LED_POLARITY_CTRL,
++                            QCA808X_LED_ACTIVE_HIGH,
++                            active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
++}
++
++static struct phy_driver at803x_driver[] = {
++{
++      /* Qualcomm Atheros AR8035 */
++      PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
++      .name                   = "Qualcomm Atheros AR8035",
++      .flags                  = PHY_POLL_CABLE_TEST,
++      .probe                  = at8035_probe,
++      .config_aneg            = at803x_config_aneg,
++      .config_init            = at803x_config_init,
++      .soft_reset             = genphy_soft_reset,
++      .set_wol                = at803x_set_wol,
++      .get_wol                = at803x_get_wol,
++      .suspend                = at803x_suspend,
++      .resume                 = at803x_resume,
++      /* PHY_GBIT_FEATURES */
++      .read_status            = at803x_read_status,
++      .config_intr            = at803x_config_intr,
++      .handle_interrupt       = at803x_handle_interrupt,
++      .get_tunable            = at803x_get_tunable,
++      .set_tunable            = at803x_set_tunable,
++      .cable_test_start       = at8031_cable_test_start,
++      .cable_test_get_status  = at8031_cable_test_get_status,
++}, {
++      /* Qualcomm Atheros AR8030 */
++      .phy_id                 = ATH8030_PHY_ID,
++      .name                   = "Qualcomm Atheros AR8030",
++      .phy_id_mask            = AT8030_PHY_ID_MASK,
++      .probe                  = at8035_probe,
++      .config_init            = at803x_config_init,
++      .link_change_notify     = at803x_link_change_notify,
++      .set_wol                = at803x_set_wol,
++      .get_wol                = at803x_get_wol,
++      .suspend                = at803x_suspend,
++      .resume                 = at803x_resume,
++      /* PHY_BASIC_FEATURES */
++      .config_intr            = at803x_config_intr,
++      .handle_interrupt       = at803x_handle_interrupt,
++}, {
++      /* Qualcomm Atheros AR8031/AR8033 */
++      PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
++      .name                   = "Qualcomm Atheros AR8031/AR8033",
++      .flags                  = PHY_POLL_CABLE_TEST,
++      .probe                  = at8031_probe,
++      .config_init            = at8031_config_init,
++      .config_aneg            = at803x_config_aneg,
++      .soft_reset             = genphy_soft_reset,
++      .set_wol                = at8031_set_wol,
++      .get_wol                = at803x_get_wol,
++      .suspend                = at803x_suspend,
++      .resume                 = at803x_resume,
++      .read_page              = at803x_read_page,
++      .write_page             = at803x_write_page,
++      .get_features           = at803x_get_features,
++      .read_status            = at8031_read_status,
++      .config_intr            = at8031_config_intr,
++      .handle_interrupt       = at803x_handle_interrupt,
++      .get_tunable            = at803x_get_tunable,
++      .set_tunable            = at803x_set_tunable,
++      .cable_test_start       = at8031_cable_test_start,
++      .cable_test_get_status  = at8031_cable_test_get_status,
++}, {
++      /* Qualcomm Atheros AR8032 */
++      PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
++      .name                   = "Qualcomm Atheros AR8032",
++      .probe                  = at803x_probe,
++      .flags                  = PHY_POLL_CABLE_TEST,
++      .config_init            = at803x_config_init,
++      .link_change_notify     = at803x_link_change_notify,
++      .suspend                = at803x_suspend,
++      .resume                 = at803x_resume,
++      /* PHY_BASIC_FEATURES */
++      .config_intr            = at803x_config_intr,
++      .handle_interrupt       = at803x_handle_interrupt,
++      .cable_test_start       = at803x_cable_test_start,
++      .cable_test_get_status  = at8032_cable_test_get_status,
++}, {
++      /* ATHEROS AR9331 */
++      PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
++      .name                   = "Qualcomm Atheros AR9331 built-in PHY",
++      .probe                  = at803x_probe,
++      .suspend                = at803x_suspend,
++      .resume                 = at803x_resume,
++      .flags                  = PHY_POLL_CABLE_TEST,
++      /* PHY_BASIC_FEATURES */
++      .config_intr            = at803x_config_intr,
++      .handle_interrupt       = at803x_handle_interrupt,
++      .cable_test_start       = at803x_cable_test_start,
++      .cable_test_get_status  = at8032_cable_test_get_status,
++      .read_status            = at803x_read_status,
++      .soft_reset             = genphy_soft_reset,
++      .config_aneg            = at803x_config_aneg,
++}, {
++      /* Qualcomm Atheros QCA9561 */
++      PHY_ID_MATCH_EXACT(QCA9561_PHY_ID),
++      .name                   = "Qualcomm Atheros QCA9561 built-in PHY",
++      .probe                  = at803x_probe,
++      .suspend                = at803x_suspend,
++      .resume                 = at803x_resume,
++      .flags                  = PHY_POLL_CABLE_TEST,
++      /* PHY_BASIC_FEATURES */
++      .config_intr            = at803x_config_intr,
++      .handle_interrupt       = at803x_handle_interrupt,
++      .cable_test_start       = at803x_cable_test_start,
++      .cable_test_get_status  = at8032_cable_test_get_status,
++      .read_status            = at803x_read_status,
++      .soft_reset             = genphy_soft_reset,
++      .config_aneg            = at803x_config_aneg,
++}, {
++      /* QCA8337 */
++      .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         = qca83xx_get_sset_count,
++      .get_strings            = qca83xx_get_strings,
++      .get_stats              = qca83xx_get_stats,
++      .suspend                = qca8337_suspend,
++      .resume                 = qca83xx_resume,
++}, {
++      /* QCA8327-A from switch QCA8327-AL1A */
++      .phy_id                 = QCA8327_A_PHY_ID,
++      .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            = qca8327_config_init,
++      .soft_reset             = genphy_soft_reset,
++      .get_sset_count         = qca83xx_get_sset_count,
++      .get_strings            = qca83xx_get_strings,
++      .get_stats              = qca83xx_get_stats,
++      .suspend                = qca8327_suspend,
++      .resume                 = qca83xx_resume,
++}, {
++      /* QCA8327-B from switch QCA8327-BL1A */
++      .phy_id                 = QCA8327_B_PHY_ID,
++      .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            = qca8327_config_init,
++      .soft_reset             = genphy_soft_reset,
++      .get_sset_count         = qca83xx_get_sset_count,
++      .get_strings            = qca83xx_get_strings,
++      .get_stats              = qca83xx_get_stats,
++      .suspend                = qca8327_suspend,
++      .resume                 = qca83xx_resume,
++}, {
++      /* Qualcomm QCA8081 */
++      PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
++      .name                   = "Qualcomm QCA8081",
++      .flags                  = PHY_POLL_CABLE_TEST,
++      .probe                  = at803x_probe,
++      .config_intr            = at803x_config_intr,
++      .handle_interrupt       = at803x_handle_interrupt,
++      .get_tunable            = at803x_get_tunable,
++      .set_tunable            = at803x_set_tunable,
++      .set_wol                = at803x_set_wol,
++      .get_wol                = at803x_get_wol,
++      .get_features           = qca808x_get_features,
++      .config_aneg            = qca808x_config_aneg,
++      .suspend                = genphy_suspend,
++      .resume                 = genphy_resume,
++      .read_status            = qca808x_read_status,
++      .config_init            = qca808x_config_init,
++      .soft_reset             = qca808x_soft_reset,
++      .cable_test_start       = qca808x_cable_test_start,
++      .cable_test_get_status  = qca808x_cable_test_get_status,
++      .link_change_notify     = qca808x_link_change_notify,
++      .led_brightness_set     = qca808x_led_brightness_set,
++      .led_blink_set          = qca808x_led_blink_set,
++      .led_hw_is_supported    = qca808x_led_hw_is_supported,
++      .led_hw_control_set     = qca808x_led_hw_control_set,
++      .led_hw_control_get     = qca808x_led_hw_control_get,
++      .led_polarity_set       = qca808x_led_polarity_set,
++}, };
++
++module_phy_driver(at803x_driver);
++
++static struct mdio_device_id __maybe_unused atheros_tbl[] = {
++      { ATH8030_PHY_ID, AT8030_PHY_ID_MASK },
++      { PHY_ID_MATCH_EXACT(ATH8031_PHY_ID) },
++      { 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_A_PHY_ID) },
++      { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
++      { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
++      { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
++      { }
++};
++
++MODULE_DEVICE_TABLE(mdio, atheros_tbl);
diff --git a/target/linux/generic/backport-6.6/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch b/target/linux/generic/backport-6.6/713-v6.9-02-net-phy-qcom-create-and-move-functions-to-shared-lib.patch
new file mode 100644 (file)
index 0000000..7d0e1f4
--- /dev/null
@@ -0,0 +1,243 @@
+From 6fb760972c49490b03f3db2ad64cf30bdd28c54a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 29 Jan 2024 15:15:20 +0100
+Subject: [PATCH 2/5] net: phy: qcom: create and move functions to shared
+ library
+
+Create and move functions to shared library in preparation for qca83xx
+PHY Family to be detached from at803x driver.
+
+Only the shared defines are moved to the shared qcom.h header.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240129141600.2592-3-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/qcom/Kconfig        |  4 ++
+ drivers/net/phy/qcom/Makefile       |  1 +
+ drivers/net/phy/qcom/at803x.c       | 69 +----------------------------
+ drivers/net/phy/qcom/qcom-phy-lib.c | 53 ++++++++++++++++++++++
+ drivers/net/phy/qcom/qcom.h         | 34 ++++++++++++++
+ 5 files changed, 94 insertions(+), 67 deletions(-)
+ create mode 100644 drivers/net/phy/qcom/qcom-phy-lib.c
+ create mode 100644 drivers/net/phy/qcom/qcom.h
+
+--- a/drivers/net/phy/qcom/Kconfig
++++ b/drivers/net/phy/qcom/Kconfig
+@@ -1,6 +1,10 @@
+ # SPDX-License-Identifier: GPL-2.0-only
++config QCOM_NET_PHYLIB
++      tristate
++
+ config AT803X_PHY
+       tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
++      select QCOM_NET_PHYLIB
+       depends on REGULATOR
+       help
+         Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
+--- a/drivers/net/phy/qcom/Makefile
++++ b/drivers/net/phy/qcom/Makefile
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
+ obj-$(CONFIG_AT803X_PHY)      += at803x.o
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -22,6 +22,8 @@
+ #include <linux/sfp.h>
+ #include <dt-bindings/net/qca-ar803x.h>
++#include "qcom.h"
++
+ #define AT803X_SPECIFIC_FUNCTION_CONTROL      0x10
+ #define AT803X_SFC_ASSERT_CRS                 BIT(11)
+ #define AT803X_SFC_FORCE_LINK                 BIT(10)
+@@ -84,9 +86,6 @@
+ #define AT803X_REG_CHIP_CONFIG                        0x1f
+ #define AT803X_BT_BX_REG_SEL                  0x8000
+-#define AT803X_DEBUG_ADDR                     0x1D
+-#define AT803X_DEBUG_DATA                     0x1E
+-
+ #define AT803X_MODE_CFG_MASK                  0x0F
+ #define AT803X_MODE_CFG_BASET_RGMII           0x00
+ #define AT803X_MODE_CFG_BASET_SGMII           0x01
+@@ -103,19 +102,6 @@
+ #define AT803X_PSSR                           0x11    /*PHY-Specific Status Register*/
+ #define AT803X_PSSR_MR_AN_COMPLETE            0x0200
+-#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_SYSTEM_CTRL_MODE         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_HIB_CTRL_PS_HIB_EN     BIT(15)
+-
+ #define AT803X_DEBUG_REG_3C                   0x3C
+ #define AT803X_DEBUG_REG_GREEN                        0x3D
+@@ -393,18 +379,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+-enum stat_access_type {
+-      PHY,
+-      MMD
+-};
+-
+-struct at803x_hw_stat {
+-      const char *string;
+-      u8 reg;
+-      u32 mask;
+-      enum stat_access_type access_type;
+-};
+-
+ static struct at803x_hw_stat qca83xx_hw_stats[] = {
+       { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
+       { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
+@@ -439,45 +413,6 @@ struct at803x_context {
+       u16 led_control;
+ };
+-static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
+-{
+-      int ret;
+-
+-      ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
+-      if (ret < 0)
+-              return ret;
+-
+-      return phy_write(phydev, AT803X_DEBUG_DATA, data);
+-}
+-
+-static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
+-{
+-      int ret;
+-
+-      ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
+-      if (ret < 0)
+-              return ret;
+-
+-      return phy_read(phydev, AT803X_DEBUG_DATA);
+-}
+-
+-static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
+-                               u16 clear, u16 set)
+-{
+-      u16 val;
+-      int ret;
+-
+-      ret = at803x_debug_reg_read(phydev, reg);
+-      if (ret < 0)
+-              return ret;
+-
+-      val = ret & 0xffff;
+-      val &= ~clear;
+-      val |= set;
+-
+-      return phy_write(phydev, AT803X_DEBUG_DATA, val);
+-}
+-
+ static int at803x_write_page(struct phy_device *phydev, int page)
+ {
+       int mask;
+--- /dev/null
++++ b/drivers/net/phy/qcom/qcom-phy-lib.c
+@@ -0,0 +1,53 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <linux/phy.h>
++#include <linux/module.h>
++
++#include "qcom.h"
++
++MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
++MODULE_AUTHOR("Matus Ujhelyi");
++MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
++MODULE_LICENSE("GPL");
++
++int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
++{
++      int ret;
++
++      ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
++      if (ret < 0)
++              return ret;
++
++      return phy_read(phydev, AT803X_DEBUG_DATA);
++}
++EXPORT_SYMBOL_GPL(at803x_debug_reg_read);
++
++int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
++                        u16 clear, u16 set)
++{
++      u16 val;
++      int ret;
++
++      ret = at803x_debug_reg_read(phydev, reg);
++      if (ret < 0)
++              return ret;
++
++      val = ret & 0xffff;
++      val &= ~clear;
++      val |= set;
++
++      return phy_write(phydev, AT803X_DEBUG_DATA, val);
++}
++EXPORT_SYMBOL_GPL(at803x_debug_reg_mask);
++
++int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
++{
++      int ret;
++
++      ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
++      if (ret < 0)
++              return ret;
++
++      return phy_write(phydev, AT803X_DEBUG_DATA, data);
++}
++EXPORT_SYMBOL_GPL(at803x_debug_reg_write);
+--- /dev/null
++++ b/drivers/net/phy/qcom/qcom.h
+@@ -0,0 +1,34 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++
++#define AT803X_DEBUG_ADDR                     0x1D
++#define AT803X_DEBUG_DATA                     0x1E
++
++#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_SYSTEM_CTRL_MODE         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_HIB_CTRL_PS_HIB_EN     BIT(15)
++
++enum stat_access_type {
++      PHY,
++      MMD
++};
++
++struct at803x_hw_stat {
++      const char *string;
++      u8 reg;
++      u32 mask;
++      enum stat_access_type access_type;
++};
++
++int at803x_debug_reg_read(struct phy_device *phydev, u16 reg);
++int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
++                        u16 clear, u16 set);
++int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data);
diff --git a/target/linux/generic/backport-6.6/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch b/target/linux/generic/backport-6.6/713-v6.9-03-net-phy-qcom-deatch-qca83xx-PHY-driver-from-at803x.patch
new file mode 100644 (file)
index 0000000..6ac09dc
--- /dev/null
@@ -0,0 +1,638 @@
+From 2e45d404d99d43bb7127b74b5dea8818df64996c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 29 Jan 2024 15:15:21 +0100
+Subject: [PATCH 3/5] net: phy: qcom: deatch qca83xx PHY driver from at803x
+
+Deatch qca83xx PHY driver from at803x.
+
+The QCA83xx PHYs implement specific function and doesn't use generic
+at803x so it can be detached from the driver and moved to a dedicated
+one.
+
+Probe function and priv struct is reimplemented to allocate and use
+only the qca83xx specific data. Unused data from at803x PHY driver
+are dropped from at803x priv struct.
+
+This is to make slimmer PHY drivers instead of including lots of bloat
+that would never be used in specific SoC.
+
+A new Kconfig flag QCA83XX_PHY is introduced to compile the new
+introduced PHY driver.
+
+As the Kconfig name starts with Qualcomm the same order is kept.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240129141600.2592-4-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/qcom/Kconfig   |  11 +-
+ drivers/net/phy/qcom/Makefile  |   1 +
+ drivers/net/phy/qcom/at803x.c  | 235 ----------------------------
+ drivers/net/phy/qcom/qca83xx.c | 275 +++++++++++++++++++++++++++++++++
+ 4 files changed, 284 insertions(+), 238 deletions(-)
+ create mode 100644 drivers/net/phy/qcom/qca83xx.c
+
+--- a/drivers/net/phy/qcom/Kconfig
++++ b/drivers/net/phy/qcom/Kconfig
+@@ -3,9 +3,14 @@ config QCOM_NET_PHYLIB
+       tristate
+ config AT803X_PHY
+-      tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
++      tristate "Qualcomm Atheros AR803X PHYs"
+       select QCOM_NET_PHYLIB
+       depends on REGULATOR
+       help
+-        Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
+-        QCA8337(Internal qca8k PHY) model
++        Currently supports the AR8030, AR8031, AR8033, AR8035 model
++
++config QCA83XX_PHY
++      tristate "Qualcomm Atheros QCA833x PHYs"
++      select QCOM_NET_PHYLIB
++      help
++        Currently supports the internal QCA8337(Internal qca8k PHY) model
+--- a/drivers/net/phy/qcom/Makefile
++++ b/drivers/net/phy/qcom/Makefile
+@@ -1,3 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
+ obj-$(CONFIG_AT803X_PHY)      += at803x.o
++obj-$(CONFIG_QCA83XX_PHY)     += qca83xx.o
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -102,17 +102,10 @@
+ #define AT803X_PSSR                           0x11    /*PHY-Specific Status Register*/
+ #define AT803X_PSSR_MR_AN_COMPLETE            0x0200
+-#define AT803X_DEBUG_REG_3C                   0x3C
+-
+-#define AT803X_DEBUG_REG_GREEN                        0x3D
+-#define   AT803X_DEBUG_GATE_CLK_IN1000                BIT(6)
+-
+ #define AT803X_DEBUG_REG_1F                   0x1F
+ #define AT803X_DEBUG_PLL_ON                   BIT(2)
+ #define AT803X_DEBUG_RGMII_1V8                        BIT(3)
+-#define MDIO_AZ_DEBUG                         0x800D
+-
+ /* AT803x supports either the XTAL input pad, an internal PLL or the
+  * DSP as clock reference for the clock output pad. The XTAL reference
+  * is only used for 25 MHz output, all other frequencies need the PLL.
+@@ -163,13 +156,7 @@
+ #define QCA8081_PHY_ID                                0x004dd101
+-#define QCA8327_A_PHY_ID                      0x004dd033
+-#define QCA8327_B_PHY_ID                      0x004dd034
+-#define QCA8337_PHY_ID                                0x004dd036
+ #define QCA9561_PHY_ID                                0x004dd042
+-#define QCA8K_PHY_ID_MASK                     0xffffffff
+-
+-#define QCA8K_DEVFLAGS_REVISION_MASK          GENMASK(2, 0)
+ #define AT803X_PAGE_FIBER                     0
+ #define AT803X_PAGE_COPPER                    1
+@@ -379,12 +366,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+-static struct at803x_hw_stat qca83xx_hw_stats[] = {
+-      { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
+-      { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
+-      { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
+-};
+-
+ struct at803x_ss_mask {
+       u16 speed_mask;
+       u8 speed_shift;
+@@ -400,7 +381,6 @@ struct at803x_priv {
+       bool is_1000basex;
+       struct regulator_dev *vddio_rdev;
+       struct regulator_dev *vddh_rdev;
+-      u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
+       int led_polarity_mode;
+ };
+@@ -564,53 +544,6 @@ static void at803x_get_wol(struct phy_de
+               wol->wolopts |= WAKE_MAGIC;
+ }
+-static int qca83xx_get_sset_count(struct phy_device *phydev)
+-{
+-      return ARRAY_SIZE(qca83xx_hw_stats);
+-}
+-
+-static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
+-{
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
+-              strscpy(data + i * ETH_GSTRING_LEN,
+-                      qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
+-      }
+-}
+-
+-static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
+-{
+-      struct at803x_hw_stat stat = qca83xx_hw_stats[i];
+-      struct at803x_priv *priv = phydev->priv;
+-      int val;
+-      u64 ret;
+-
+-      if (stat.access_type == MMD)
+-              val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
+-      else
+-              val = phy_read(phydev, stat.reg);
+-
+-      if (val < 0) {
+-              ret = U64_MAX;
+-      } else {
+-              val = val & stat.mask;
+-              priv->stats[i] += val;
+-              ret = priv->stats[i];
+-      }
+-
+-      return ret;
+-}
+-
+-static void qca83xx_get_stats(struct phy_device *phydev,
+-                            struct ethtool_stats *stats, u64 *data)
+-{
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
+-              data[i] = qca83xx_get_stat(phydev, i);
+-}
+-
+ static int at803x_suspend(struct phy_device *phydev)
+ {
+       int value;
+@@ -1707,124 +1640,6 @@ static int at8035_probe(struct phy_devic
+       return at8035_parse_dt(phydev);
+ }
+-static int qca83xx_config_init(struct phy_device *phydev)
+-{
+-      u8 switch_revision;
+-
+-      switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
+-
+-      switch (switch_revision) {
+-      case 1:
+-              /* For 100M waveform */
+-              at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
+-              /* Turn on Gigabit clock */
+-              at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
+-              break;
+-
+-      case 2:
+-              phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
+-              fallthrough;
+-      case 4:
+-              phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
+-              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;
+-      }
+-
+-      /* Following original QCA sourcecode set port to prefer master */
+-      phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
+-
+-      return 0;
+-}
+-
+-static int qca8327_config_init(struct phy_device *phydev)
+-{
+-      /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
+-       * Disable on init and enable only with 100m speed following
+-       * qca original source code.
+-       */
+-      at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
+-                            QCA8327_DEBUG_MANU_CTRL_EN, 0);
+-
+-      return qca83xx_config_init(phydev);
+-}
+-
+-static void qca83xx_link_change_notify(struct phy_device *phydev)
+-{
+-      /* 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_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_ANALOG_TEST_CTRL,
+-                                    QCA8327_DEBUG_MANU_CTRL_EN, 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;
+-
+-      usleep_range(1000, 2000);
+-
+-      return 0;
+-}
+-
+-static int qca83xx_suspend(struct phy_device *phydev)
+-{
+-      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,
+-                            AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
+-                            AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
+-
+-      return 0;
+-}
+-
+-static int qca8337_suspend(struct phy_device *phydev)
+-{
+-      /* Only QCA8337 support actual suspend. */
+-      genphy_suspend(phydev);
+-
+-      return qca83xx_suspend(phydev);
+-}
+-
+-static int qca8327_suspend(struct phy_device *phydev)
+-{
+-      u16 mask = 0;
+-
+-      /* QCA8327 cause port unreliability when phy suspend
+-       * is set.
+-       */
+-      mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
+-      phy_modify(phydev, MII_BMCR, mask, 0);
+-
+-      return qca83xx_suspend(phydev);
+-}
+-
+ static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
+ {
+       int ret;
+@@ -2599,53 +2414,6 @@ static struct phy_driver at803x_driver[]
+       .soft_reset             = genphy_soft_reset,
+       .config_aneg            = at803x_config_aneg,
+ }, {
+-      /* QCA8337 */
+-      .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         = qca83xx_get_sset_count,
+-      .get_strings            = qca83xx_get_strings,
+-      .get_stats              = qca83xx_get_stats,
+-      .suspend                = qca8337_suspend,
+-      .resume                 = qca83xx_resume,
+-}, {
+-      /* QCA8327-A from switch QCA8327-AL1A */
+-      .phy_id                 = QCA8327_A_PHY_ID,
+-      .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            = qca8327_config_init,
+-      .soft_reset             = genphy_soft_reset,
+-      .get_sset_count         = qca83xx_get_sset_count,
+-      .get_strings            = qca83xx_get_strings,
+-      .get_stats              = qca83xx_get_stats,
+-      .suspend                = qca8327_suspend,
+-      .resume                 = qca83xx_resume,
+-}, {
+-      /* QCA8327-B from switch QCA8327-BL1A */
+-      .phy_id                 = QCA8327_B_PHY_ID,
+-      .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            = qca8327_config_init,
+-      .soft_reset             = genphy_soft_reset,
+-      .get_sset_count         = qca83xx_get_sset_count,
+-      .get_strings            = qca83xx_get_strings,
+-      .get_stats              = qca83xx_get_stats,
+-      .suspend                = qca8327_suspend,
+-      .resume                 = qca83xx_resume,
+-}, {
+       /* Qualcomm QCA8081 */
+       PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
+       .name                   = "Qualcomm QCA8081",
+@@ -2683,9 +2451,6 @@ 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_A_PHY_ID) },
+-      { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
+       { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) },
+       { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
+       { }
+--- /dev/null
++++ b/drivers/net/phy/qcom/qca83xx.c
+@@ -0,0 +1,275 @@
++// SPDX-License-Identifier: GPL-2.0+
++
++#include <linux/phy.h>
++#include <linux/module.h>
++
++#include "qcom.h"
++
++#define AT803X_DEBUG_REG_3C                   0x3C
++
++#define AT803X_DEBUG_REG_GREEN                        0x3D
++#define   AT803X_DEBUG_GATE_CLK_IN1000                BIT(6)
++
++#define MDIO_AZ_DEBUG                         0x800D
++
++#define QCA8327_A_PHY_ID                      0x004dd033
++#define QCA8327_B_PHY_ID                      0x004dd034
++#define QCA8337_PHY_ID                                0x004dd036
++#define QCA8K_PHY_ID_MASK                     0xffffffff
++
++#define QCA8K_DEVFLAGS_REVISION_MASK          GENMASK(2, 0)
++
++static struct at803x_hw_stat qca83xx_hw_stats[] = {
++      { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
++      { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
++      { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
++};
++
++struct qca83xx_priv {
++      u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
++};
++
++MODULE_DESCRIPTION("Qualcomm Atheros QCA83XX PHY driver");
++MODULE_AUTHOR("Matus Ujhelyi");
++MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
++MODULE_LICENSE("GPL");
++
++static int qca83xx_get_sset_count(struct phy_device *phydev)
++{
++      return ARRAY_SIZE(qca83xx_hw_stats);
++}
++
++static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
++              strscpy(data + i * ETH_GSTRING_LEN,
++                      qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
++      }
++}
++
++static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
++{
++      struct at803x_hw_stat stat = qca83xx_hw_stats[i];
++      struct qca83xx_priv *priv = phydev->priv;
++      int val;
++      u64 ret;
++
++      if (stat.access_type == MMD)
++              val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
++      else
++              val = phy_read(phydev, stat.reg);
++
++      if (val < 0) {
++              ret = U64_MAX;
++      } else {
++              val = val & stat.mask;
++              priv->stats[i] += val;
++              ret = priv->stats[i];
++      }
++
++      return ret;
++}
++
++static void qca83xx_get_stats(struct phy_device *phydev,
++                            struct ethtool_stats *stats, u64 *data)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
++              data[i] = qca83xx_get_stat(phydev, i);
++}
++
++static int qca83xx_probe(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      struct qca83xx_priv *priv;
++
++      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      phydev->priv = priv;
++
++      return 0;
++}
++
++static int qca83xx_config_init(struct phy_device *phydev)
++{
++      u8 switch_revision;
++
++      switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
++
++      switch (switch_revision) {
++      case 1:
++              /* For 100M waveform */
++              at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
++              /* Turn on Gigabit clock */
++              at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
++              break;
++
++      case 2:
++              phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
++              fallthrough;
++      case 4:
++              phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
++              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;
++      }
++
++      /* Following original QCA sourcecode set port to prefer master */
++      phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
++
++      return 0;
++}
++
++static int qca8327_config_init(struct phy_device *phydev)
++{
++      /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
++       * Disable on init and enable only with 100m speed following
++       * qca original source code.
++       */
++      at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
++                            QCA8327_DEBUG_MANU_CTRL_EN, 0);
++
++      return qca83xx_config_init(phydev);
++}
++
++static void qca83xx_link_change_notify(struct phy_device *phydev)
++{
++      /* 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_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_ANALOG_TEST_CTRL,
++                                    QCA8327_DEBUG_MANU_CTRL_EN, 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;
++
++      usleep_range(1000, 2000);
++
++      return 0;
++}
++
++static int qca83xx_suspend(struct phy_device *phydev)
++{
++      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,
++                            AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
++                            AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
++
++      return 0;
++}
++
++static int qca8337_suspend(struct phy_device *phydev)
++{
++      /* Only QCA8337 support actual suspend. */
++      genphy_suspend(phydev);
++
++      return qca83xx_suspend(phydev);
++}
++
++static int qca8327_suspend(struct phy_device *phydev)
++{
++      u16 mask = 0;
++
++      /* QCA8327 cause port unreliability when phy suspend
++       * is set.
++       */
++      mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
++      phy_modify(phydev, MII_BMCR, mask, 0);
++
++      return qca83xx_suspend(phydev);
++}
++
++static struct phy_driver qca83xx_driver[] = {
++{
++      /* QCA8337 */
++      .phy_id                 = QCA8337_PHY_ID,
++      .phy_id_mask            = QCA8K_PHY_ID_MASK,
++      .name                   = "Qualcomm Atheros 8337 internal PHY",
++      /* PHY_GBIT_FEATURES */
++      .probe                  = qca83xx_probe,
++      .flags                  = PHY_IS_INTERNAL,
++      .config_init            = qca83xx_config_init,
++      .soft_reset             = genphy_soft_reset,
++      .get_sset_count         = qca83xx_get_sset_count,
++      .get_strings            = qca83xx_get_strings,
++      .get_stats              = qca83xx_get_stats,
++      .suspend                = qca8337_suspend,
++      .resume                 = qca83xx_resume,
++}, {
++      /* QCA8327-A from switch QCA8327-AL1A */
++      .phy_id                 = QCA8327_A_PHY_ID,
++      .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                  = qca83xx_probe,
++      .flags                  = PHY_IS_INTERNAL,
++      .config_init            = qca8327_config_init,
++      .soft_reset             = genphy_soft_reset,
++      .get_sset_count         = qca83xx_get_sset_count,
++      .get_strings            = qca83xx_get_strings,
++      .get_stats              = qca83xx_get_stats,
++      .suspend                = qca8327_suspend,
++      .resume                 = qca83xx_resume,
++}, {
++      /* QCA8327-B from switch QCA8327-BL1A */
++      .phy_id                 = QCA8327_B_PHY_ID,
++      .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                  = qca83xx_probe,
++      .flags                  = PHY_IS_INTERNAL,
++      .config_init            = qca8327_config_init,
++      .soft_reset             = genphy_soft_reset,
++      .get_sset_count         = qca83xx_get_sset_count,
++      .get_strings            = qca83xx_get_strings,
++      .get_stats              = qca83xx_get_stats,
++      .suspend                = qca8327_suspend,
++      .resume                 = qca83xx_resume,
++}, };
++
++module_phy_driver(qca83xx_driver);
++
++static struct mdio_device_id __maybe_unused qca83xx_tbl[] = {
++      { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
++      { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
++      { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
++      { }
++};
++
++MODULE_DEVICE_TABLE(mdio, qca83xx_tbl);
diff --git a/target/linux/generic/backport-6.6/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch b/target/linux/generic/backport-6.6/713-v6.9-04-net-phy-qcom-move-additional-functions-to-shared-lib.patch
new file mode 100644 (file)
index 0000000..9c43ad1
--- /dev/null
@@ -0,0 +1,1014 @@
+From 249d2b80e4db0e38503ed0ec2af6c7401bc099b9 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 29 Jan 2024 15:15:22 +0100
+Subject: [PATCH 4/5] net: phy: qcom: move additional functions to shared
+ library
+
+Move additional functions to shared library in preparation for qca808x
+PHY Family to be detached from at803x driver.
+
+Only the shared defines are moved to the shared qcom.h header.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240129141600.2592-5-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/qcom/at803x.c       | 426 +---------------------------
+ drivers/net/phy/qcom/qcom-phy-lib.c | 376 ++++++++++++++++++++++++
+ drivers/net/phy/qcom/qcom.h         |  86 ++++++
+ 3 files changed, 463 insertions(+), 425 deletions(-)
+
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -24,65 +24,11 @@
+ #include "qcom.h"
+-#define AT803X_SPECIFIC_FUNCTION_CONTROL      0x10
+-#define AT803X_SFC_ASSERT_CRS                 BIT(11)
+-#define AT803X_SFC_FORCE_LINK                 BIT(10)
+-#define AT803X_SFC_MDI_CROSSOVER_MODE_M               GENMASK(6, 5)
+-#define AT803X_SFC_AUTOMATIC_CROSSOVER                0x3
+-#define AT803X_SFC_MANUAL_MDIX                        0x1
+-#define AT803X_SFC_MANUAL_MDI                 0x0
+-#define AT803X_SFC_SQE_TEST                   BIT(2)
+-#define AT803X_SFC_POLARITY_REVERSAL          BIT(1)
+-#define AT803X_SFC_DISABLE_JABBER             BIT(0)
+-
+-#define AT803X_SPECIFIC_STATUS                        0x11
+-#define AT803X_SS_SPEED_MASK                  GENMASK(15, 14)
+-#define AT803X_SS_SPEED_1000                  2
+-#define AT803X_SS_SPEED_100                   1
+-#define AT803X_SS_SPEED_10                    0
+-#define AT803X_SS_DUPLEX                      BIT(13)
+-#define AT803X_SS_SPEED_DUPLEX_RESOLVED               BIT(11)
+-#define AT803X_SS_MDIX                                BIT(6)
+-
+-#define QCA808X_SS_SPEED_MASK                 GENMASK(9, 7)
+-#define QCA808X_SS_SPEED_2500                 4
+-
+-#define AT803X_INTR_ENABLE                    0x12
+-#define AT803X_INTR_ENABLE_AUTONEG_ERR                BIT(15)
+-#define AT803X_INTR_ENABLE_SPEED_CHANGED      BIT(14)
+-#define AT803X_INTR_ENABLE_DUPLEX_CHANGED     BIT(13)
+-#define AT803X_INTR_ENABLE_PAGE_RECEIVED      BIT(12)
+-#define AT803X_INTR_ENABLE_LINK_FAIL          BIT(11)
+-#define AT803X_INTR_ENABLE_LINK_SUCCESS               BIT(10)
+-#define AT803X_INTR_ENABLE_LINK_FAIL_BX               BIT(8)
+-#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX    BIT(7)
+-#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE        BIT(5)
+-#define AT803X_INTR_ENABLE_POLARITY_CHANGED   BIT(1)
+-#define AT803X_INTR_ENABLE_WOL                        BIT(0)
+-
+-#define AT803X_INTR_STATUS                    0x13
+-
+-#define AT803X_SMART_SPEED                    0x14
+-#define AT803X_SMART_SPEED_ENABLE             BIT(5)
+-#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK   GENMASK(4, 2)
+-#define AT803X_SMART_SPEED_BYPASS_TIMER               BIT(1)
+-#define AT803X_CDT                            0x16
+-#define AT803X_CDT_MDI_PAIR_MASK              GENMASK(9, 8)
+-#define AT803X_CDT_ENABLE_TEST                        BIT(0)
+-#define AT803X_CDT_STATUS                     0x1c
+-#define AT803X_CDT_STATUS_STAT_NORMAL         0
+-#define AT803X_CDT_STATUS_STAT_SHORT          1
+-#define AT803X_CDT_STATUS_STAT_OPEN           2
+-#define AT803X_CDT_STATUS_STAT_FAIL           3
+-#define AT803X_CDT_STATUS_STAT_MASK           GENMASK(9, 8)
+-#define AT803X_CDT_STATUS_DELTA_TIME_MASK     GENMASK(7, 0)
+ #define AT803X_LED_CONTROL                    0x18
+ #define AT803X_PHY_MMD3_WOL_CTRL              0x8012
+ #define AT803X_WOL_EN                         BIT(5)
+-#define AT803X_LOC_MAC_ADDR_0_15_OFFSET               0x804C
+-#define AT803X_LOC_MAC_ADDR_16_31_OFFSET      0x804B
+-#define AT803X_LOC_MAC_ADDR_32_47_OFFSET      0x804A
++
+ #define AT803X_REG_CHIP_CONFIG                        0x1f
+ #define AT803X_BT_BX_REG_SEL                  0x8000
+@@ -138,10 +84,6 @@
+ #define AT803X_CLK_OUT_STRENGTH_HALF          1
+ #define AT803X_CLK_OUT_STRENGTH_QUARTER               2
+-#define AT803X_DEFAULT_DOWNSHIFT              5
+-#define AT803X_MIN_DOWNSHIFT                  2
+-#define AT803X_MAX_DOWNSHIFT                  9
+-
+ #define AT803X_MMD3_SMARTEEE_CTL1             0x805b
+ #define AT803X_MMD3_SMARTEEE_CTL2             0x805c
+ #define AT803X_MMD3_SMARTEEE_CTL3             0x805d
+@@ -366,11 +308,6 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR8
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+-struct at803x_ss_mask {
+-      u16 speed_mask;
+-      u8 speed_shift;
+-};
+-
+ struct at803x_priv {
+       int flags;
+       u16 clk_25m_reg;
+@@ -470,80 +407,6 @@ static void at803x_context_restore(struc
+       phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
+ }
+-static int at803x_set_wol(struct phy_device *phydev,
+-                        struct ethtool_wolinfo *wol)
+-{
+-      int ret, irq_enabled;
+-
+-      if (wol->wolopts & WAKE_MAGIC) {
+-              struct net_device *ndev = phydev->attached_dev;
+-              const u8 *mac;
+-              unsigned int i;
+-              static const unsigned int offsets[] = {
+-                      AT803X_LOC_MAC_ADDR_32_47_OFFSET,
+-                      AT803X_LOC_MAC_ADDR_16_31_OFFSET,
+-                      AT803X_LOC_MAC_ADDR_0_15_OFFSET,
+-              };
+-
+-              if (!ndev)
+-                      return -ENODEV;
+-
+-              mac = (const u8 *)ndev->dev_addr;
+-
+-              if (!is_valid_ether_addr(mac))
+-                      return -EINVAL;
+-
+-              for (i = 0; i < 3; i++)
+-                      phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
+-                                    mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
+-
+-              /* Enable WOL interrupt */
+-              ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
+-              if (ret)
+-                      return ret;
+-      } else {
+-              /* Disable WOL interrupt */
+-              ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
+-              if (ret)
+-                      return ret;
+-      }
+-
+-      /* Clear WOL status */
+-      ret = phy_read(phydev, AT803X_INTR_STATUS);
+-      if (ret < 0)
+-              return ret;
+-
+-      /* Check if there are other interrupts except for WOL triggered when PHY is
+-       * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
+-       * be passed up to the interrupt PIN.
+-       */
+-      irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
+-      if (irq_enabled < 0)
+-              return irq_enabled;
+-
+-      irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
+-      if (ret & irq_enabled && !phy_polling_mode(phydev))
+-              phy_trigger_machine(phydev);
+-
+-      return 0;
+-}
+-
+-static void at803x_get_wol(struct phy_device *phydev,
+-                         struct ethtool_wolinfo *wol)
+-{
+-      int value;
+-
+-      wol->supported = WAKE_MAGIC;
+-      wol->wolopts = 0;
+-
+-      value = phy_read(phydev, AT803X_INTR_ENABLE);
+-      if (value < 0)
+-              return;
+-
+-      if (value & AT803X_INTR_ENABLE_WOL)
+-              wol->wolopts |= WAKE_MAGIC;
+-}
+-
+ static int at803x_suspend(struct phy_device *phydev)
+ {
+       int value;
+@@ -816,73 +679,6 @@ static int at803x_config_init(struct phy
+       return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
+ }
+-static int at803x_ack_interrupt(struct phy_device *phydev)
+-{
+-      int err;
+-
+-      err = phy_read(phydev, AT803X_INTR_STATUS);
+-
+-      return (err < 0) ? err : 0;
+-}
+-
+-static int at803x_config_intr(struct phy_device *phydev)
+-{
+-      int err;
+-      int value;
+-
+-      value = phy_read(phydev, AT803X_INTR_ENABLE);
+-
+-      if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+-              /* Clear any pending interrupts */
+-              err = at803x_ack_interrupt(phydev);
+-              if (err)
+-                      return err;
+-
+-              value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
+-              value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
+-              value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
+-              value |= AT803X_INTR_ENABLE_LINK_FAIL;
+-              value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
+-
+-              err = phy_write(phydev, AT803X_INTR_ENABLE, value);
+-      } else {
+-              err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
+-              if (err)
+-                      return err;
+-
+-              /* Clear any pending interrupts */
+-              err = at803x_ack_interrupt(phydev);
+-      }
+-
+-      return err;
+-}
+-
+-static irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
+-{
+-      int irq_status, int_enabled;
+-
+-      irq_status = phy_read(phydev, AT803X_INTR_STATUS);
+-      if (irq_status < 0) {
+-              phy_error(phydev);
+-              return IRQ_NONE;
+-      }
+-
+-      /* Read the current enabled interrupts */
+-      int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
+-      if (int_enabled < 0) {
+-              phy_error(phydev);
+-              return IRQ_NONE;
+-      }
+-
+-      /* See if this was one of our enabled interrupts */
+-      if (!(irq_status & int_enabled))
+-              return IRQ_NONE;
+-
+-      phy_trigger_machine(phydev);
+-
+-      return IRQ_HANDLED;
+-}
+-
+ static void at803x_link_change_notify(struct phy_device *phydev)
+ {
+       /*
+@@ -908,69 +704,6 @@ static void at803x_link_change_notify(st
+       }
+ }
+-static int at803x_read_specific_status(struct phy_device *phydev,
+-                                     struct at803x_ss_mask ss_mask)
+-{
+-      int ss;
+-
+-      /* Read the AT8035 PHY-Specific Status register, which indicates the
+-       * speed and duplex that the PHY is actually using, irrespective of
+-       * whether we are in autoneg mode or not.
+-       */
+-      ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
+-      if (ss < 0)
+-              return ss;
+-
+-      if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
+-              int sfc, speed;
+-
+-              sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
+-              if (sfc < 0)
+-                      return sfc;
+-
+-              speed = ss & ss_mask.speed_mask;
+-              speed >>= ss_mask.speed_shift;
+-
+-              switch (speed) {
+-              case AT803X_SS_SPEED_10:
+-                      phydev->speed = SPEED_10;
+-                      break;
+-              case AT803X_SS_SPEED_100:
+-                      phydev->speed = SPEED_100;
+-                      break;
+-              case AT803X_SS_SPEED_1000:
+-                      phydev->speed = SPEED_1000;
+-                      break;
+-              case QCA808X_SS_SPEED_2500:
+-                      phydev->speed = SPEED_2500;
+-                      break;
+-              }
+-              if (ss & AT803X_SS_DUPLEX)
+-                      phydev->duplex = DUPLEX_FULL;
+-              else
+-                      phydev->duplex = DUPLEX_HALF;
+-
+-              if (ss & AT803X_SS_MDIX)
+-                      phydev->mdix = ETH_TP_MDI_X;
+-              else
+-                      phydev->mdix = ETH_TP_MDI;
+-
+-              switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
+-              case AT803X_SFC_MANUAL_MDI:
+-                      phydev->mdix_ctrl = ETH_TP_MDI;
+-                      break;
+-              case AT803X_SFC_MANUAL_MDIX:
+-                      phydev->mdix_ctrl = ETH_TP_MDI_X;
+-                      break;
+-              case AT803X_SFC_AUTOMATIC_CROSSOVER:
+-                      phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
+-                      break;
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+ static int at803x_read_status(struct phy_device *phydev)
+ {
+       struct at803x_ss_mask ss_mask = { 0 };
+@@ -1006,50 +739,6 @@ static int at803x_read_status(struct phy
+       return 0;
+ }
+-static int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
+-{
+-      u16 val;
+-
+-      switch (ctrl) {
+-      case ETH_TP_MDI:
+-              val = AT803X_SFC_MANUAL_MDI;
+-              break;
+-      case ETH_TP_MDI_X:
+-              val = AT803X_SFC_MANUAL_MDIX;
+-              break;
+-      case ETH_TP_MDI_AUTO:
+-              val = AT803X_SFC_AUTOMATIC_CROSSOVER;
+-              break;
+-      default:
+-              return 0;
+-      }
+-
+-      return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
+-                        AT803X_SFC_MDI_CROSSOVER_MODE_M,
+-                        FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
+-}
+-
+-static int at803x_prepare_config_aneg(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
+-      if (ret < 0)
+-              return ret;
+-
+-      /* Changes of the midx bits are disruptive to the normal operation;
+-       * therefore any changes to these registers must be followed by a
+-       * software reset to take effect.
+-       */
+-      if (ret == 1) {
+-              ret = genphy_soft_reset(phydev);
+-              if (ret < 0)
+-                      return ret;
+-      }
+-
+-      return 0;
+-}
+-
+ static int at803x_config_aneg(struct phy_device *phydev)
+ {
+       struct at803x_priv *priv = phydev->priv;
+@@ -1065,80 +754,6 @@ static int at803x_config_aneg(struct phy
+       return genphy_config_aneg(phydev);
+ }
+-static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
+-{
+-      int val;
+-
+-      val = phy_read(phydev, AT803X_SMART_SPEED);
+-      if (val < 0)
+-              return val;
+-
+-      if (val & AT803X_SMART_SPEED_ENABLE)
+-              *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
+-      else
+-              *d = DOWNSHIFT_DEV_DISABLE;
+-
+-      return 0;
+-}
+-
+-static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
+-{
+-      u16 mask, set;
+-      int ret;
+-
+-      switch (cnt) {
+-      case DOWNSHIFT_DEV_DEFAULT_COUNT:
+-              cnt = AT803X_DEFAULT_DOWNSHIFT;
+-              fallthrough;
+-      case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
+-              set = AT803X_SMART_SPEED_ENABLE |
+-                    AT803X_SMART_SPEED_BYPASS_TIMER |
+-                    FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
+-              mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
+-              break;
+-      case DOWNSHIFT_DEV_DISABLE:
+-              set = 0;
+-              mask = AT803X_SMART_SPEED_ENABLE |
+-                     AT803X_SMART_SPEED_BYPASS_TIMER;
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
+-
+-      /* After changing the smart speed settings, we need to perform a
+-       * software reset, use phy_init_hw() to make sure we set the
+-       * reapply any values which might got lost during software reset.
+-       */
+-      if (ret == 1)
+-              ret = phy_init_hw(phydev);
+-
+-      return ret;
+-}
+-
+-static int at803x_get_tunable(struct phy_device *phydev,
+-                            struct ethtool_tunable *tuna, void *data)
+-{
+-      switch (tuna->id) {
+-      case ETHTOOL_PHY_DOWNSHIFT:
+-              return at803x_get_downshift(phydev, data);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+-static int at803x_set_tunable(struct phy_device *phydev,
+-                            struct ethtool_tunable *tuna, const void *data)
+-{
+-      switch (tuna->id) {
+-      case ETHTOOL_PHY_DOWNSHIFT:
+-              return at803x_set_downshift(phydev, *(const u8 *)data);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+ static int at803x_cable_test_result_trans(u16 status)
+ {
+       switch (FIELD_GET(AT803X_CDT_STATUS_STAT_MASK, status)) {
+@@ -1170,45 +785,6 @@ static bool at803x_cdt_fault_length_vali
+       return false;
+ }
+-static int at803x_cdt_fault_length(int dt)
+-{
+-      /* According to the datasheet the distance to the fault is
+-       * DELTA_TIME * 0.824 meters.
+-       *
+-       * The author suspect the correct formula is:
+-       *
+-       *   fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
+-       *
+-       * where c is the speed of light, VF is the velocity factor of
+-       * the twisted pair cable, 125MHz the counter frequency and
+-       * we need to divide by 2 because the hardware will measure the
+-       * round trip time to the fault and back to the PHY.
+-       *
+-       * With a VF of 0.69 we get the factor 0.824 mentioned in the
+-       * datasheet.
+-       */
+-      return (dt * 824) / 10;
+-}
+-
+-static int at803x_cdt_start(struct phy_device *phydev,
+-                          u32 cdt_start)
+-{
+-      return phy_write(phydev, AT803X_CDT, cdt_start);
+-}
+-
+-static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
+-                                        u32 cdt_en)
+-{
+-      int val, ret;
+-
+-      /* One test run takes about 25ms */
+-      ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
+-                                  !(val & cdt_en),
+-                                  30000, 100000, true);
+-
+-      return ret < 0 ? ret : 0;
+-}
+-
+ static int at803x_cable_test_one_pair(struct phy_device *phydev, int pair)
+ {
+       static const int ethtool_pair[] = {
+--- a/drivers/net/phy/qcom/qcom-phy-lib.c
++++ b/drivers/net/phy/qcom/qcom-phy-lib.c
+@@ -3,6 +3,9 @@
+ #include <linux/phy.h>
+ #include <linux/module.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++
+ #include "qcom.h"
+ MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
+@@ -51,3 +54,376 @@ int at803x_debug_reg_write(struct phy_de
+       return phy_write(phydev, AT803X_DEBUG_DATA, data);
+ }
+ EXPORT_SYMBOL_GPL(at803x_debug_reg_write);
++
++int at803x_set_wol(struct phy_device *phydev,
++                 struct ethtool_wolinfo *wol)
++{
++      int ret, irq_enabled;
++
++      if (wol->wolopts & WAKE_MAGIC) {
++              struct net_device *ndev = phydev->attached_dev;
++              const u8 *mac;
++              unsigned int i;
++              static const unsigned int offsets[] = {
++                      AT803X_LOC_MAC_ADDR_32_47_OFFSET,
++                      AT803X_LOC_MAC_ADDR_16_31_OFFSET,
++                      AT803X_LOC_MAC_ADDR_0_15_OFFSET,
++              };
++
++              if (!ndev)
++                      return -ENODEV;
++
++              mac = (const u8 *)ndev->dev_addr;
++
++              if (!is_valid_ether_addr(mac))
++                      return -EINVAL;
++
++              for (i = 0; i < 3; i++)
++                      phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
++                                    mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
++
++              /* Enable WOL interrupt */
++              ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
++              if (ret)
++                      return ret;
++      } else {
++              /* Disable WOL interrupt */
++              ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
++              if (ret)
++                      return ret;
++      }
++
++      /* Clear WOL status */
++      ret = phy_read(phydev, AT803X_INTR_STATUS);
++      if (ret < 0)
++              return ret;
++
++      /* Check if there are other interrupts except for WOL triggered when PHY is
++       * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
++       * be passed up to the interrupt PIN.
++       */
++      irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
++      if (irq_enabled < 0)
++              return irq_enabled;
++
++      irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
++      if (ret & irq_enabled && !phy_polling_mode(phydev))
++              phy_trigger_machine(phydev);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(at803x_set_wol);
++
++void at803x_get_wol(struct phy_device *phydev,
++                  struct ethtool_wolinfo *wol)
++{
++      int value;
++
++      wol->supported = WAKE_MAGIC;
++      wol->wolopts = 0;
++
++      value = phy_read(phydev, AT803X_INTR_ENABLE);
++      if (value < 0)
++              return;
++
++      if (value & AT803X_INTR_ENABLE_WOL)
++              wol->wolopts |= WAKE_MAGIC;
++}
++EXPORT_SYMBOL_GPL(at803x_get_wol);
++
++int at803x_ack_interrupt(struct phy_device *phydev)
++{
++      int err;
++
++      err = phy_read(phydev, AT803X_INTR_STATUS);
++
++      return (err < 0) ? err : 0;
++}
++EXPORT_SYMBOL_GPL(at803x_ack_interrupt);
++
++int at803x_config_intr(struct phy_device *phydev)
++{
++      int err;
++      int value;
++
++      value = phy_read(phydev, AT803X_INTR_ENABLE);
++
++      if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
++              /* Clear any pending interrupts */
++              err = at803x_ack_interrupt(phydev);
++              if (err)
++                      return err;
++
++              value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
++              value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
++              value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
++              value |= AT803X_INTR_ENABLE_LINK_FAIL;
++              value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
++
++              err = phy_write(phydev, AT803X_INTR_ENABLE, value);
++      } else {
++              err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
++              if (err)
++                      return err;
++
++              /* Clear any pending interrupts */
++              err = at803x_ack_interrupt(phydev);
++      }
++
++      return err;
++}
++EXPORT_SYMBOL_GPL(at803x_config_intr);
++
++irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
++{
++      int irq_status, int_enabled;
++
++      irq_status = phy_read(phydev, AT803X_INTR_STATUS);
++      if (irq_status < 0) {
++              phy_error(phydev);
++              return IRQ_NONE;
++      }
++
++      /* Read the current enabled interrupts */
++      int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
++      if (int_enabled < 0) {
++              phy_error(phydev);
++              return IRQ_NONE;
++      }
++
++      /* See if this was one of our enabled interrupts */
++      if (!(irq_status & int_enabled))
++              return IRQ_NONE;
++
++      phy_trigger_machine(phydev);
++
++      return IRQ_HANDLED;
++}
++EXPORT_SYMBOL_GPL(at803x_handle_interrupt);
++
++int at803x_read_specific_status(struct phy_device *phydev,
++                              struct at803x_ss_mask ss_mask)
++{
++      int ss;
++
++      /* Read the AT8035 PHY-Specific Status register, which indicates the
++       * speed and duplex that the PHY is actually using, irrespective of
++       * whether we are in autoneg mode or not.
++       */
++      ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
++      if (ss < 0)
++              return ss;
++
++      if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
++              int sfc, speed;
++
++              sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
++              if (sfc < 0)
++                      return sfc;
++
++              speed = ss & ss_mask.speed_mask;
++              speed >>= ss_mask.speed_shift;
++
++              switch (speed) {
++              case AT803X_SS_SPEED_10:
++                      phydev->speed = SPEED_10;
++                      break;
++              case AT803X_SS_SPEED_100:
++                      phydev->speed = SPEED_100;
++                      break;
++              case AT803X_SS_SPEED_1000:
++                      phydev->speed = SPEED_1000;
++                      break;
++              case QCA808X_SS_SPEED_2500:
++                      phydev->speed = SPEED_2500;
++                      break;
++              }
++              if (ss & AT803X_SS_DUPLEX)
++                      phydev->duplex = DUPLEX_FULL;
++              else
++                      phydev->duplex = DUPLEX_HALF;
++
++              if (ss & AT803X_SS_MDIX)
++                      phydev->mdix = ETH_TP_MDI_X;
++              else
++                      phydev->mdix = ETH_TP_MDI;
++
++              switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
++              case AT803X_SFC_MANUAL_MDI:
++                      phydev->mdix_ctrl = ETH_TP_MDI;
++                      break;
++              case AT803X_SFC_MANUAL_MDIX:
++                      phydev->mdix_ctrl = ETH_TP_MDI_X;
++                      break;
++              case AT803X_SFC_AUTOMATIC_CROSSOVER:
++                      phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
++                      break;
++              }
++      }
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(at803x_read_specific_status);
++
++int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
++{
++      u16 val;
++
++      switch (ctrl) {
++      case ETH_TP_MDI:
++              val = AT803X_SFC_MANUAL_MDI;
++              break;
++      case ETH_TP_MDI_X:
++              val = AT803X_SFC_MANUAL_MDIX;
++              break;
++      case ETH_TP_MDI_AUTO:
++              val = AT803X_SFC_AUTOMATIC_CROSSOVER;
++              break;
++      default:
++              return 0;
++      }
++
++      return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
++                        AT803X_SFC_MDI_CROSSOVER_MODE_M,
++                        FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
++}
++EXPORT_SYMBOL_GPL(at803x_config_mdix);
++
++int at803x_prepare_config_aneg(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
++      if (ret < 0)
++              return ret;
++
++      /* Changes of the midx bits are disruptive to the normal operation;
++       * therefore any changes to these registers must be followed by a
++       * software reset to take effect.
++       */
++      if (ret == 1) {
++              ret = genphy_soft_reset(phydev);
++              if (ret < 0)
++                      return ret;
++      }
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
++
++static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
++{
++      int val;
++
++      val = phy_read(phydev, AT803X_SMART_SPEED);
++      if (val < 0)
++              return val;
++
++      if (val & AT803X_SMART_SPEED_ENABLE)
++              *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
++      else
++              *d = DOWNSHIFT_DEV_DISABLE;
++
++      return 0;
++}
++
++static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
++{
++      u16 mask, set;
++      int ret;
++
++      switch (cnt) {
++      case DOWNSHIFT_DEV_DEFAULT_COUNT:
++              cnt = AT803X_DEFAULT_DOWNSHIFT;
++              fallthrough;
++      case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
++              set = AT803X_SMART_SPEED_ENABLE |
++                    AT803X_SMART_SPEED_BYPASS_TIMER |
++                    FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
++              mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
++              break;
++      case DOWNSHIFT_DEV_DISABLE:
++              set = 0;
++              mask = AT803X_SMART_SPEED_ENABLE |
++                     AT803X_SMART_SPEED_BYPASS_TIMER;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
++
++      /* After changing the smart speed settings, we need to perform a
++       * software reset, use phy_init_hw() to make sure we set the
++       * reapply any values which might got lost during software reset.
++       */
++      if (ret == 1)
++              ret = phy_init_hw(phydev);
++
++      return ret;
++}
++
++int at803x_get_tunable(struct phy_device *phydev,
++                     struct ethtool_tunable *tuna, void *data)
++{
++      switch (tuna->id) {
++      case ETHTOOL_PHY_DOWNSHIFT:
++              return at803x_get_downshift(phydev, data);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++EXPORT_SYMBOL_GPL(at803x_get_tunable);
++
++int at803x_set_tunable(struct phy_device *phydev,
++                     struct ethtool_tunable *tuna, const void *data)
++{
++      switch (tuna->id) {
++      case ETHTOOL_PHY_DOWNSHIFT:
++              return at803x_set_downshift(phydev, *(const u8 *)data);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++EXPORT_SYMBOL_GPL(at803x_set_tunable);
++
++int at803x_cdt_fault_length(int dt)
++{
++      /* According to the datasheet the distance to the fault is
++       * DELTA_TIME * 0.824 meters.
++       *
++       * The author suspect the correct formula is:
++       *
++       *   fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
++       *
++       * where c is the speed of light, VF is the velocity factor of
++       * the twisted pair cable, 125MHz the counter frequency and
++       * we need to divide by 2 because the hardware will measure the
++       * round trip time to the fault and back to the PHY.
++       *
++       * With a VF of 0.69 we get the factor 0.824 mentioned in the
++       * datasheet.
++       */
++      return (dt * 824) / 10;
++}
++EXPORT_SYMBOL_GPL(at803x_cdt_fault_length);
++
++int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start)
++{
++      return phy_write(phydev, AT803X_CDT, cdt_start);
++}
++EXPORT_SYMBOL_GPL(at803x_cdt_start);
++
++int at803x_cdt_wait_for_completion(struct phy_device *phydev,
++                                 u32 cdt_en)
++{
++      int val, ret;
++
++      /* One test run takes about 25ms */
++      ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
++                                  !(val & cdt_en),
++                                  30000, 100000, true);
++
++      return ret < 0 ? ret : 0;
++}
++EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
+--- a/drivers/net/phy/qcom/qcom.h
++++ b/drivers/net/phy/qcom/qcom.h
+@@ -1,5 +1,63 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
++#define AT803X_SPECIFIC_FUNCTION_CONTROL      0x10
++#define AT803X_SFC_ASSERT_CRS                 BIT(11)
++#define AT803X_SFC_FORCE_LINK                 BIT(10)
++#define AT803X_SFC_MDI_CROSSOVER_MODE_M               GENMASK(6, 5)
++#define AT803X_SFC_AUTOMATIC_CROSSOVER                0x3
++#define AT803X_SFC_MANUAL_MDIX                        0x1
++#define AT803X_SFC_MANUAL_MDI                 0x0
++#define AT803X_SFC_SQE_TEST                   BIT(2)
++#define AT803X_SFC_POLARITY_REVERSAL          BIT(1)
++#define AT803X_SFC_DISABLE_JABBER             BIT(0)
++
++#define AT803X_SPECIFIC_STATUS                        0x11
++#define AT803X_SS_SPEED_MASK                  GENMASK(15, 14)
++#define AT803X_SS_SPEED_1000                  2
++#define AT803X_SS_SPEED_100                   1
++#define AT803X_SS_SPEED_10                    0
++#define AT803X_SS_DUPLEX                      BIT(13)
++#define AT803X_SS_SPEED_DUPLEX_RESOLVED               BIT(11)
++#define AT803X_SS_MDIX                                BIT(6)
++
++#define QCA808X_SS_SPEED_MASK                 GENMASK(9, 7)
++#define QCA808X_SS_SPEED_2500                 4
++
++#define AT803X_INTR_ENABLE                    0x12
++#define AT803X_INTR_ENABLE_AUTONEG_ERR                BIT(15)
++#define AT803X_INTR_ENABLE_SPEED_CHANGED      BIT(14)
++#define AT803X_INTR_ENABLE_DUPLEX_CHANGED     BIT(13)
++#define AT803X_INTR_ENABLE_PAGE_RECEIVED      BIT(12)
++#define AT803X_INTR_ENABLE_LINK_FAIL          BIT(11)
++#define AT803X_INTR_ENABLE_LINK_SUCCESS               BIT(10)
++#define AT803X_INTR_ENABLE_LINK_FAIL_BX               BIT(8)
++#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX    BIT(7)
++#define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE        BIT(5)
++#define AT803X_INTR_ENABLE_POLARITY_CHANGED   BIT(1)
++#define AT803X_INTR_ENABLE_WOL                        BIT(0)
++
++#define AT803X_INTR_STATUS                    0x13
++
++#define AT803X_SMART_SPEED                    0x14
++#define AT803X_SMART_SPEED_ENABLE             BIT(5)
++#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK   GENMASK(4, 2)
++#define AT803X_SMART_SPEED_BYPASS_TIMER               BIT(1)
++
++#define AT803X_CDT                            0x16
++#define AT803X_CDT_MDI_PAIR_MASK              GENMASK(9, 8)
++#define AT803X_CDT_ENABLE_TEST                        BIT(0)
++#define AT803X_CDT_STATUS                     0x1c
++#define AT803X_CDT_STATUS_STAT_NORMAL         0
++#define AT803X_CDT_STATUS_STAT_SHORT          1
++#define AT803X_CDT_STATUS_STAT_OPEN           2
++#define AT803X_CDT_STATUS_STAT_FAIL           3
++#define AT803X_CDT_STATUS_STAT_MASK           GENMASK(9, 8)
++#define AT803X_CDT_STATUS_DELTA_TIME_MASK     GENMASK(7, 0)
++
++#define AT803X_LOC_MAC_ADDR_0_15_OFFSET               0x804C
++#define AT803X_LOC_MAC_ADDR_16_31_OFFSET      0x804B
++#define AT803X_LOC_MAC_ADDR_32_47_OFFSET      0x804A
++
+ #define AT803X_DEBUG_ADDR                     0x1D
+ #define AT803X_DEBUG_DATA                     0x1E
+@@ -16,6 +74,10 @@
+ #define   AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE BIT(13)
+ #define   AT803X_DEBUG_HIB_CTRL_PS_HIB_EN     BIT(15)
++#define AT803X_DEFAULT_DOWNSHIFT              5
++#define AT803X_MIN_DOWNSHIFT                  2
++#define AT803X_MAX_DOWNSHIFT                  9
++
+ enum stat_access_type {
+       PHY,
+       MMD
+@@ -28,7 +90,31 @@ struct at803x_hw_stat {
+       enum stat_access_type access_type;
+ };
++struct at803x_ss_mask {
++      u16 speed_mask;
++      u8 speed_shift;
++};
++
+ int at803x_debug_reg_read(struct phy_device *phydev, u16 reg);
+ int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
+                         u16 clear, u16 set);
+ int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data);
++int at803x_set_wol(struct phy_device *phydev,
++                 struct ethtool_wolinfo *wol);
++void at803x_get_wol(struct phy_device *phydev,
++                  struct ethtool_wolinfo *wol);
++int at803x_ack_interrupt(struct phy_device *phydev);
++int at803x_config_intr(struct phy_device *phydev);
++irqreturn_t at803x_handle_interrupt(struct phy_device *phydev);
++int at803x_read_specific_status(struct phy_device *phydev,
++                              struct at803x_ss_mask ss_mask);
++int at803x_config_mdix(struct phy_device *phydev, u8 ctrl);
++int at803x_prepare_config_aneg(struct phy_device *phydev);
++int at803x_get_tunable(struct phy_device *phydev,
++                     struct ethtool_tunable *tuna, void *data);
++int at803x_set_tunable(struct phy_device *phydev,
++                     struct ethtool_tunable *tuna, const void *data);
++int at803x_cdt_fault_length(int dt);
++int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start);
++int at803x_cdt_wait_for_completion(struct phy_device *phydev,
++                                 u32 cdt_en);
diff --git a/target/linux/generic/backport-6.6/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch b/target/linux/generic/backport-6.6/713-v6.9-05-net-phy-qcom-detach-qca808x-PHY-driver-from-at803x.patch
new file mode 100644 (file)
index 0000000..d8092a8
--- /dev/null
@@ -0,0 +1,1936 @@
+From c89414adf2ec7cd9e7080c419aa5847f1db1009c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 29 Jan 2024 15:15:23 +0100
+Subject: [PATCH 5/5] net: phy: qcom: detach qca808x PHY driver from at803x
+
+Almost all the QCA8081 PHY driver OPs are specific and only some of them
+use the generic at803x.
+
+To make the at803x code slimmer, move all the specific qca808x regs and
+functions to a dedicated PHY driver.
+
+Probe function and priv struct is reworked to allocate and use only the
+qca808x specific data. Unused data from at803x PHY driver are dropped
+from at803x priv struct.
+
+Also a new Kconfig is introduced QCA808X_PHY, to compile the newly
+introduced PHY driver for QCA8081 PHY.
+
+As the Kconfig name starts with Qualcomm the same order is kept.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240129141600.2592-6-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/qcom/Kconfig   |   6 +
+ drivers/net/phy/qcom/Makefile  |   1 +
+ drivers/net/phy/qcom/at803x.c  | 897 +------------------------------
+ drivers/net/phy/qcom/qca808x.c | 934 +++++++++++++++++++++++++++++++++
+ 4 files changed, 942 insertions(+), 896 deletions(-)
+ create mode 100644 drivers/net/phy/qcom/qca808x.c
+
+--- a/drivers/net/phy/qcom/Kconfig
++++ b/drivers/net/phy/qcom/Kconfig
+@@ -14,3 +14,9 @@ config QCA83XX_PHY
+       select QCOM_NET_PHYLIB
+       help
+         Currently supports the internal QCA8337(Internal qca8k PHY) model
++
++config QCA808X_PHY
++      tristate "Qualcomm QCA808x PHYs"
++      select QCOM_NET_PHYLIB
++      help
++        Currently supports the QCA8081 model
+--- a/drivers/net/phy/qcom/Makefile
++++ b/drivers/net/phy/qcom/Makefile
+@@ -2,3 +2,4 @@
+ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
+ obj-$(CONFIG_AT803X_PHY)      += at803x.o
+ obj-$(CONFIG_QCA83XX_PHY)     += qca83xx.o
++obj-$(CONFIG_QCA808X_PHY)     += qca808x.o
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -96,8 +96,6 @@
+ #define ATH8035_PHY_ID                                0x004dd072
+ #define AT8030_PHY_ID_MASK                    0xffffffef
+-#define QCA8081_PHY_ID                                0x004dd101
+-
+ #define QCA9561_PHY_ID                                0x004dd042
+ #define AT803X_PAGE_FIBER                     0
+@@ -110,201 +108,7 @@
+ /* disable hibernation mode */
+ #define AT803X_DISABLE_HIBERNATION_MODE               BIT(2)
+-/* ADC threshold */
+-#define QCA808X_PHY_DEBUG_ADC_THRESHOLD               0x2c80
+-#define QCA808X_ADC_THRESHOLD_MASK            GENMASK(7, 0)
+-#define QCA808X_ADC_THRESHOLD_80MV            0
+-#define QCA808X_ADC_THRESHOLD_100MV           0xf0
+-#define QCA808X_ADC_THRESHOLD_200MV           0x0f
+-#define QCA808X_ADC_THRESHOLD_300MV           0xff
+-
+-/* CLD control */
+-#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7               0x8007
+-#define QCA808X_8023AZ_AFE_CTRL_MASK          GENMASK(8, 4)
+-#define QCA808X_8023AZ_AFE_EN                 0x90
+-
+-/* AZ control */
+-#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL     0x8008
+-#define QCA808X_MMD3_AZ_TRAINING_VAL          0x1c32
+-
+-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB   0x8014
+-#define QCA808X_MSE_THRESHOLD_20DB_VALUE      0x529
+-
+-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB   0x800E
+-#define QCA808X_MSE_THRESHOLD_17DB_VALUE      0x341
+-
+-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB   0x801E
+-#define QCA808X_MSE_THRESHOLD_27DB_VALUE      0x419
+-
+-#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB   0x8020
+-#define QCA808X_MSE_THRESHOLD_28DB_VALUE      0x341
+-
+-#define QCA808X_PHY_MMD7_TOP_OPTION1          0x901c
+-#define QCA808X_TOP_OPTION1_DATA              0x0
+-
+-#define QCA808X_PHY_MMD3_DEBUG_1              0xa100
+-#define QCA808X_MMD3_DEBUG_1_VALUE            0x9203
+-#define QCA808X_PHY_MMD3_DEBUG_2              0xa101
+-#define QCA808X_MMD3_DEBUG_2_VALUE            0x48ad
+-#define QCA808X_PHY_MMD3_DEBUG_3              0xa103
+-#define QCA808X_MMD3_DEBUG_3_VALUE            0x1698
+-#define QCA808X_PHY_MMD3_DEBUG_4              0xa105
+-#define QCA808X_MMD3_DEBUG_4_VALUE            0x8001
+-#define QCA808X_PHY_MMD3_DEBUG_5              0xa106
+-#define QCA808X_MMD3_DEBUG_5_VALUE            0x1111
+-#define QCA808X_PHY_MMD3_DEBUG_6              0xa011
+-#define QCA808X_MMD3_DEBUG_6_VALUE            0x5f85
+-
+-/* master/slave seed config */
+-#define QCA808X_PHY_DEBUG_LOCAL_SEED          9
+-#define QCA808X_MASTER_SLAVE_SEED_ENABLE      BIT(1)
+-#define QCA808X_MASTER_SLAVE_SEED_CFG         GENMASK(12, 2)
+-#define QCA808X_MASTER_SLAVE_SEED_RANGE               0x32
+-
+-/* Hibernation yields lower power consumpiton in contrast with normal operation mode.
+- * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.
+- */
+-#define QCA808X_DBG_AN_TEST                   0xb
+-#define QCA808X_HIBERNATION_EN                        BIT(15)
+-
+-#define QCA808X_CDT_ENABLE_TEST                       BIT(15)
+-#define QCA808X_CDT_INTER_CHECK_DIS           BIT(13)
+-#define QCA808X_CDT_STATUS                    BIT(11)
+-#define QCA808X_CDT_LENGTH_UNIT                       BIT(10)
+-
+-#define QCA808X_MMD3_CDT_STATUS                       0x8064
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_A          0x8065
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_B          0x8066
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_C          0x8067
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_D          0x8068
+-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT    GENMASK(15, 8)
+-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT   GENMASK(7, 0)
+-
+-#define QCA808X_CDT_CODE_PAIR_A                       GENMASK(15, 12)
+-#define QCA808X_CDT_CODE_PAIR_B                       GENMASK(11, 8)
+-#define QCA808X_CDT_CODE_PAIR_C                       GENMASK(7, 4)
+-#define QCA808X_CDT_CODE_PAIR_D                       GENMASK(3, 0)
+-
+-#define QCA808X_CDT_STATUS_STAT_TYPE          GENMASK(1, 0)
+-#define QCA808X_CDT_STATUS_STAT_FAIL          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
+-#define QCA808X_CDT_STATUS_STAT_NORMAL                FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
+-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN     FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
+-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT    FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
+-
+-#define QCA808X_CDT_STATUS_STAT_MDI           GENMASK(3, 2)
+-#define QCA808X_CDT_STATUS_STAT_MDI1          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
+-#define QCA808X_CDT_STATUS_STAT_MDI2          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
+-#define QCA808X_CDT_STATUS_STAT_MDI3          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
+-
+-/* NORMAL are MDI with type set to 0 */
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI1
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI2
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI3
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
+-
+-/* Added for reference of existence but should be handled by wait_for_completion already */
+-#define QCA808X_CDT_STATUS_STAT_BUSY          (BIT(1) | BIT(3))
+-
+-#define QCA808X_MMD7_LED_GLOBAL                       0x8073
+-#define QCA808X_LED_BLINK_1                   GENMASK(11, 6)
+-#define QCA808X_LED_BLINK_2                   GENMASK(5, 0)
+-/* Values are the same for both BLINK_1 and BLINK_2 */
+-#define QCA808X_LED_BLINK_FREQ_MASK           GENMASK(5, 3)
+-#define QCA808X_LED_BLINK_FREQ_2HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
+-#define QCA808X_LED_BLINK_FREQ_4HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
+-#define QCA808X_LED_BLINK_FREQ_8HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
+-#define QCA808X_LED_BLINK_FREQ_16HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
+-#define QCA808X_LED_BLINK_FREQ_32HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
+-#define QCA808X_LED_BLINK_FREQ_64HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
+-#define QCA808X_LED_BLINK_FREQ_128HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
+-#define QCA808X_LED_BLINK_FREQ_256HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
+-#define QCA808X_LED_BLINK_DUTY_MASK           GENMASK(2, 0)
+-#define QCA808X_LED_BLINK_DUTY_50_50          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
+-#define QCA808X_LED_BLINK_DUTY_75_25          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
+-#define QCA808X_LED_BLINK_DUTY_25_75          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
+-#define QCA808X_LED_BLINK_DUTY_33_67          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
+-#define QCA808X_LED_BLINK_DUTY_67_33          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
+-#define QCA808X_LED_BLINK_DUTY_17_83          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
+-#define QCA808X_LED_BLINK_DUTY_83_17          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
+-#define QCA808X_LED_BLINK_DUTY_8_92           FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
+-
+-#define QCA808X_MMD7_LED2_CTRL                        0x8074
+-#define QCA808X_MMD7_LED2_FORCE_CTRL          0x8075
+-#define QCA808X_MMD7_LED1_CTRL                        0x8076
+-#define QCA808X_MMD7_LED1_FORCE_CTRL          0x8077
+-#define QCA808X_MMD7_LED0_CTRL                        0x8078
+-#define QCA808X_MMD7_LED_CTRL(x)              (0x8078 - ((x) * 2))
+-
+-/* LED hw control pattern is the same for every LED */
+-#define QCA808X_LED_PATTERN_MASK              GENMASK(15, 0)
+-#define QCA808X_LED_SPEED2500_ON              BIT(15)
+-#define QCA808X_LED_SPEED2500_BLINK           BIT(14)
+-/* Follow blink trigger even if duplex or speed condition doesn't match */
+-#define QCA808X_LED_BLINK_CHECK_BYPASS                BIT(13)
+-#define QCA808X_LED_FULL_DUPLEX_ON            BIT(12)
+-#define QCA808X_LED_HALF_DUPLEX_ON            BIT(11)
+-#define QCA808X_LED_TX_BLINK                  BIT(10)
+-#define QCA808X_LED_RX_BLINK                  BIT(9)
+-#define QCA808X_LED_TX_ON_10MS                        BIT(8)
+-#define QCA808X_LED_RX_ON_10MS                        BIT(7)
+-#define QCA808X_LED_SPEED1000_ON              BIT(6)
+-#define QCA808X_LED_SPEED100_ON                       BIT(5)
+-#define QCA808X_LED_SPEED10_ON                        BIT(4)
+-#define QCA808X_LED_COLLISION_BLINK           BIT(3)
+-#define QCA808X_LED_SPEED1000_BLINK           BIT(2)
+-#define QCA808X_LED_SPEED100_BLINK            BIT(1)
+-#define QCA808X_LED_SPEED10_BLINK             BIT(0)
+-
+-#define QCA808X_MMD7_LED0_FORCE_CTRL          0x8079
+-#define QCA808X_MMD7_LED_FORCE_CTRL(x)                (0x8079 - ((x) * 2))
+-
+-/* LED force ctrl is the same for every LED
+- * No documentation exist for this, not even internal one
+- * with NDA as QCOM gives only info about configuring
+- * hw control pattern rules and doesn't indicate any way
+- * to force the LED to specific mode.
+- * These define comes from reverse and testing and maybe
+- * lack of some info or some info are not entirely correct.
+- * For the basic LED control and hw control these finding
+- * are enough to support LED control in all the required APIs.
+- *
+- * On doing some comparison with implementation with qca807x,
+- * it was found that it's 1:1 equal to it and confirms all the
+- * reverse done. It was also found further specification with the
+- * force mode and the blink modes.
+- */
+-#define QCA808X_LED_FORCE_EN                  BIT(15)
+-#define QCA808X_LED_FORCE_MODE_MASK           GENMASK(14, 13)
+-#define QCA808X_LED_FORCE_BLINK_1             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
+-#define QCA808X_LED_FORCE_BLINK_2             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
+-#define QCA808X_LED_FORCE_ON                  FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
+-#define QCA808X_LED_FORCE_OFF                 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
+-
+-#define QCA808X_MMD7_LED_POLARITY_CTRL                0x901a
+-/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
+- * LED to active high. It's not clear what BIT 3 and BIT 4 does.
+- */
+-#define QCA808X_LED_ACTIVE_HIGH                       BIT(6)
+-
+-/* QCA808X 1G chip type */
+-#define QCA808X_PHY_MMD7_CHIP_TYPE            0x901d
+-#define QCA808X_PHY_CHIP_TYPE_1G              BIT(0)
+-
+-#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL     0x9072
+-#define QCA8081_PHY_FIFO_RSTN                 BIT(11)
+-
+-MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
++MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi");
+ MODULE_LICENSE("GPL");
+@@ -318,7 +122,6 @@ struct at803x_priv {
+       bool is_1000basex;
+       struct regulator_dev *vddio_rdev;
+       struct regulator_dev *vddh_rdev;
+-      int led_polarity_mode;
+ };
+ struct at803x_context {
+@@ -519,9 +322,6 @@ static int at803x_probe(struct phy_devic
+       if (!priv)
+               return -ENOMEM;
+-      /* Init LED polarity mode to -1 */
+-      priv->led_polarity_mode = -1;
+-
+       phydev->priv = priv;
+       ret = at803x_parse_dt(phydev);
+@@ -1216,672 +1016,6 @@ static int at8035_probe(struct phy_devic
+       return at8035_parse_dt(phydev);
+ }
+-static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      /* Enable fast retrain */
+-      ret = genphy_c45_fast_retrain(phydev, true);
+-      if (ret)
+-              return ret;
+-
+-      phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
+-                    QCA808X_TOP_OPTION1_DATA);
+-      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
+-                    QCA808X_MSE_THRESHOLD_20DB_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
+-                    QCA808X_MSE_THRESHOLD_17DB_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
+-                    QCA808X_MSE_THRESHOLD_27DB_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
+-                    QCA808X_MSE_THRESHOLD_28DB_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
+-                    QCA808X_MMD3_DEBUG_1_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
+-                    QCA808X_MMD3_DEBUG_4_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
+-                    QCA808X_MMD3_DEBUG_5_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
+-                    QCA808X_MMD3_DEBUG_3_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
+-                    QCA808X_MMD3_DEBUG_6_VALUE);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
+-                    QCA808X_MMD3_DEBUG_2_VALUE);
+-
+-      return 0;
+-}
+-
+-static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
+-{
+-      u16 seed_value;
+-
+-      if (!enable)
+-              return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
+-                              QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
+-
+-      seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE);
+-      return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
+-                      QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
+-                      FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
+-                      QCA808X_MASTER_SLAVE_SEED_ENABLE);
+-}
+-
+-static bool qca808x_is_prefer_master(struct phy_device *phydev)
+-{
+-      return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
+-              (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
+-}
+-
+-static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
+-{
+-      return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+-}
+-
+-static int qca808x_config_init(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      /* Active adc&vga on 802.3az for the link 1000M and 100M */
+-      ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
+-                           QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
+-      if (ret)
+-              return ret;
+-
+-      /* Adjust the threshold on 802.3az for the link 1000M */
+-      ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
+-                          QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
+-                          QCA808X_MMD3_AZ_TRAINING_VAL);
+-      if (ret)
+-              return ret;
+-
+-      if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
+-              /* Config the fast retrain for the link 2500M */
+-              ret = qca808x_phy_fast_retrain_config(phydev);
+-              if (ret)
+-                      return ret;
+-
+-              ret = genphy_read_master_slave(phydev);
+-              if (ret < 0)
+-                      return ret;
+-
+-              if (!qca808x_is_prefer_master(phydev)) {
+-                      /* Enable seed and configure lower ramdom seed to make phy
+-                       * linked as slave mode.
+-                       */
+-                      ret = qca808x_phy_ms_seed_enable(phydev, true);
+-                      if (ret)
+-                              return ret;
+-              }
+-      }
+-
+-      /* Configure adc threshold as 100mv for the link 10M */
+-      return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
+-                                   QCA808X_ADC_THRESHOLD_MASK,
+-                                   QCA808X_ADC_THRESHOLD_100MV);
+-}
+-
+-static int qca808x_read_status(struct phy_device *phydev)
+-{
+-      struct at803x_ss_mask ss_mask = { 0 };
+-      int ret;
+-
+-      ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+-      if (ret < 0)
+-              return ret;
+-
+-      linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
+-                       ret & MDIO_AN_10GBT_STAT_LP2_5G);
+-
+-      ret = genphy_read_status(phydev);
+-      if (ret)
+-              return ret;
+-
+-      /* qca8081 takes the different bits for speed value from at803x */
+-      ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
+-      ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
+-      ret = at803x_read_specific_status(phydev, ss_mask);
+-      if (ret < 0)
+-              return ret;
+-
+-      if (phydev->link) {
+-              if (phydev->speed == SPEED_2500)
+-                      phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+-              else
+-                      phydev->interface = PHY_INTERFACE_MODE_SGMII;
+-      } else {
+-              /* generate seed as a lower random value to make PHY linked as SLAVE easily,
+-               * except for master/slave configuration fault detected or the master mode
+-               * preferred.
+-               *
+-               * the reason for not putting this code into the function link_change_notify is
+-               * the corner case where the link partner is also the qca8081 PHY and the seed
+-               * value is configured as the same value, the link can't be up and no link change
+-               * occurs.
+-               */
+-              if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
+-                      if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
+-                          qca808x_is_prefer_master(phydev)) {
+-                              qca808x_phy_ms_seed_enable(phydev, false);
+-                      } else {
+-                              qca808x_phy_ms_seed_enable(phydev, true);
+-                      }
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+-static int qca808x_soft_reset(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      ret = genphy_soft_reset(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      if (qca808x_has_fast_retrain_or_slave_seed(phydev))
+-              ret = qca808x_phy_ms_seed_enable(phydev, true);
+-
+-      return ret;
+-}
+-
+-static bool qca808x_cdt_fault_length_valid(int cdt_code)
+-{
+-      switch (cdt_code) {
+-      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+-              return true;
+-      default:
+-              return false;
+-      }
+-}
+-
+-static int qca808x_cable_test_result_trans(int cdt_code)
+-{
+-      switch (cdt_code) {
+-      case QCA808X_CDT_STATUS_STAT_NORMAL:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+-      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+-      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
+-      case QCA808X_CDT_STATUS_STAT_FAIL:
+-      default:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+-      }
+-}
+-
+-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
+-                                  int result)
+-{
+-      int val;
+-      u32 cdt_length_reg = 0;
+-
+-      switch (pair) {
+-      case ETHTOOL_A_CABLE_PAIR_A:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_B:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_C:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_D:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
+-      if (val < 0)
+-              return val;
+-
+-      if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
+-              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
+-      else
+-              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
+-
+-      return at803x_cdt_fault_length(val);
+-}
+-
+-static int qca808x_cable_test_start(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      /* perform CDT with the following configs:
+-       * 1. disable hibernation.
+-       * 2. force PHY working in MDI mode.
+-       * 3. for PHY working in 1000BaseT.
+-       * 4. configure the threshold.
+-       */
+-
+-      ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = at803x_config_mdix(phydev, ETH_TP_MDI);
+-      if (ret < 0)
+-              return ret;
+-
+-      /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */
+-      phydev->duplex = DUPLEX_FULL;
+-      phydev->speed = SPEED_1000;
+-      ret = genphy_c45_pma_setup_forced(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = genphy_setup_forced(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      /* configure the thresholds for open, short, pair ok test */
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
+-      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
+-
+-      return 0;
+-}
+-
+-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
+-                                            u16 status)
+-{
+-      int length, result;
+-      u16 pair_code;
+-
+-      switch (pair) {
+-      case ETHTOOL_A_CABLE_PAIR_A:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_B:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_C:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_D:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      result = qca808x_cable_test_result_trans(pair_code);
+-      ethnl_cable_test_result(phydev, pair, result);
+-
+-      if (qca808x_cdt_fault_length_valid(pair_code)) {
+-              length = qca808x_cdt_fault_length(phydev, pair, result);
+-              ethnl_cable_test_fault_length(phydev, pair, length);
+-      }
+-
+-      return 0;
+-}
+-
+-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
+-{
+-      int ret, val;
+-
+-      *finished = false;
+-
+-      val = QCA808X_CDT_ENABLE_TEST |
+-            QCA808X_CDT_LENGTH_UNIT;
+-      ret = at803x_cdt_start(phydev, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
+-      if (ret)
+-              return ret;
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
+-      if (val < 0)
+-              return val;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
+-      if (ret)
+-              return ret;
+-
+-      *finished = true;
+-
+-      return 0;
+-}
+-
+-static int qca808x_get_features(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      ret = genphy_c45_pma_read_abilities(phydev);
+-      if (ret)
+-              return ret;
+-
+-      /* The autoneg ability is not existed in bit3 of MMD7.1,
+-       * but it is supported by qca808x PHY, so we add it here
+-       * manually.
+-       */
+-      linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
+-
+-      /* As for the qca8081 1G version chip, the 2500baseT ability is also
+-       * existed in the bit0 of MMD1.21, we need to remove it manually if
+-       * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
+-       */
+-      ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
+-      if (ret < 0)
+-              return ret;
+-
+-      if (QCA808X_PHY_CHIP_TYPE_1G & ret)
+-              linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+-
+-      return 0;
+-}
+-
+-static int qca808x_config_aneg(struct phy_device *phydev)
+-{
+-      int phy_ctrl = 0;
+-      int ret;
+-
+-      ret = at803x_prepare_config_aneg(phydev);
+-      if (ret)
+-              return ret;
+-
+-      /* The reg MII_BMCR also needs to be configured for force mode, the
+-       * genphy_config_aneg is also needed.
+-       */
+-      if (phydev->autoneg == AUTONEG_DISABLE)
+-              genphy_c45_pma_setup_forced(phydev);
+-
+-      if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
+-              phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
+-
+-      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+-                                   MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
+-      if (ret < 0)
+-              return ret;
+-
+-      return __genphy_config_aneg(phydev, ret);
+-}
+-
+-static void qca808x_link_change_notify(struct phy_device *phydev)
+-{
+-      /* Assert interface sgmii fifo on link down, deassert it on link up,
+-       * the interface device address is always phy address added by 1.
+-       */
+-      mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
+-                                 MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
+-                                 QCA8081_PHY_FIFO_RSTN,
+-                                 phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
+-}
+-
+-static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
+-                                  u16 *offload_trigger)
+-{
+-      /* Parsing specific to netdev trigger */
+-      if (test_bit(TRIGGER_NETDEV_TX, &rules))
+-              *offload_trigger |= QCA808X_LED_TX_BLINK;
+-      if (test_bit(TRIGGER_NETDEV_RX, &rules))
+-              *offload_trigger |= QCA808X_LED_RX_BLINK;
+-      if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
+-              *offload_trigger |= QCA808X_LED_SPEED10_ON;
+-      if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
+-              *offload_trigger |= QCA808X_LED_SPEED100_ON;
+-      if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
+-              *offload_trigger |= QCA808X_LED_SPEED1000_ON;
+-      if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
+-              *offload_trigger |= QCA808X_LED_SPEED2500_ON;
+-      if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
+-              *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
+-      if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
+-              *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
+-
+-      if (rules && !*offload_trigger)
+-              return -EOPNOTSUPP;
+-
+-      /* Enable BLINK_CHECK_BYPASS by default to make the LED
+-       * blink even with duplex or speed mode not enabled.
+-       */
+-      *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
+-
+-      return 0;
+-}
+-
+-static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
+-{
+-      u16 reg;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
+-                                QCA808X_LED_FORCE_EN);
+-}
+-
+-static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
+-                                     unsigned long rules)
+-{
+-      u16 offload_trigger = 0;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
+-}
+-
+-static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
+-                                    unsigned long rules)
+-{
+-      u16 reg, offload_trigger = 0;
+-      int ret;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_CTRL(index);
+-
+-      ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_led_hw_control_enable(phydev, index);
+-      if (ret)
+-              return ret;
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-                            QCA808X_LED_PATTERN_MASK,
+-                            offload_trigger);
+-}
+-
+-static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
+-{
+-      u16 reg;
+-      int val;
+-
+-      if (index > 2)
+-              return false;
+-
+-      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
+-
+-      return !(val & QCA808X_LED_FORCE_EN);
+-}
+-
+-static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
+-                                    unsigned long *rules)
+-{
+-      u16 reg;
+-      int val;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      /* Check if we have hw control enabled */
+-      if (qca808x_led_hw_control_status(phydev, index))
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_CTRL(index);
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
+-      if (val & QCA808X_LED_TX_BLINK)
+-              set_bit(TRIGGER_NETDEV_TX, rules);
+-      if (val & QCA808X_LED_RX_BLINK)
+-              set_bit(TRIGGER_NETDEV_RX, rules);
+-      if (val & QCA808X_LED_SPEED10_ON)
+-              set_bit(TRIGGER_NETDEV_LINK_10, rules);
+-      if (val & QCA808X_LED_SPEED100_ON)
+-              set_bit(TRIGGER_NETDEV_LINK_100, rules);
+-      if (val & QCA808X_LED_SPEED1000_ON)
+-              set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+-      if (val & QCA808X_LED_SPEED2500_ON)
+-              set_bit(TRIGGER_NETDEV_LINK_2500, rules);
+-      if (val & QCA808X_LED_HALF_DUPLEX_ON)
+-              set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
+-      if (val & QCA808X_LED_FULL_DUPLEX_ON)
+-              set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
+-
+-      return 0;
+-}
+-
+-static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
+-{
+-      u16 reg;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_CTRL(index);
+-
+-      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
+-                                QCA808X_LED_PATTERN_MASK);
+-}
+-
+-static int qca808x_led_brightness_set(struct phy_device *phydev,
+-                                    u8 index, enum led_brightness value)
+-{
+-      u16 reg;
+-      int ret;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      if (!value) {
+-              ret = qca808x_led_hw_control_reset(phydev, index);
+-              if (ret)
+-                      return ret;
+-      }
+-
+-      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-                            QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
+-                            QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
+-                                                           QCA808X_LED_FORCE_OFF);
+-}
+-
+-static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
+-                               unsigned long *delay_on,
+-                               unsigned long *delay_off)
+-{
+-      int ret;
+-      u16 reg;
+-
+-      if (index > 2)
+-              return -EINVAL;
+-
+-      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      /* Set blink to 50% off, 50% on at 4Hz by default */
+-      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
+-                           QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
+-                           QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
+-      if (ret)
+-              return ret;
+-
+-      /* We use BLINK_1 for normal blinking */
+-      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
+-                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
+-      if (ret)
+-              return ret;
+-
+-      /* We set blink to 4Hz, aka 250ms */
+-      *delay_on = 250 / 2;
+-      *delay_off = 250 / 2;
+-
+-      return 0;
+-}
+-
+-static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
+-                                  unsigned long modes)
+-{
+-      struct at803x_priv *priv = phydev->priv;
+-      bool active_low = false;
+-      u32 mode;
+-
+-      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
+-              switch (mode) {
+-              case PHY_LED_ACTIVE_LOW:
+-                      active_low = true;
+-                      break;
+-              default:
+-                      return -EINVAL;
+-              }
+-      }
+-
+-      /* PHY polarity is global and can't be set per LED.
+-       * To detect this, check if last requested polarity mode
+-       * match the new one.
+-       */
+-      if (priv->led_polarity_mode >= 0 &&
+-          priv->led_polarity_mode != active_low) {
+-              phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
+-              return -EINVAL;
+-      }
+-
+-      /* Save the last PHY polarity mode */
+-      priv->led_polarity_mode = active_low;
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_AN,
+-                            QCA808X_MMD7_LED_POLARITY_CTRL,
+-                            QCA808X_LED_ACTIVE_HIGH,
+-                            active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
+-}
+-
+ static struct phy_driver at803x_driver[] = {
+ {
+       /* Qualcomm Atheros AR8035 */
+@@ -1989,34 +1123,6 @@ static struct phy_driver at803x_driver[]
+       .read_status            = at803x_read_status,
+       .soft_reset             = genphy_soft_reset,
+       .config_aneg            = at803x_config_aneg,
+-}, {
+-      /* Qualcomm QCA8081 */
+-      PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
+-      .name                   = "Qualcomm QCA8081",
+-      .flags                  = PHY_POLL_CABLE_TEST,
+-      .probe                  = at803x_probe,
+-      .config_intr            = at803x_config_intr,
+-      .handle_interrupt       = at803x_handle_interrupt,
+-      .get_tunable            = at803x_get_tunable,
+-      .set_tunable            = at803x_set_tunable,
+-      .set_wol                = at803x_set_wol,
+-      .get_wol                = at803x_get_wol,
+-      .get_features           = qca808x_get_features,
+-      .config_aneg            = qca808x_config_aneg,
+-      .suspend                = genphy_suspend,
+-      .resume                 = genphy_resume,
+-      .read_status            = qca808x_read_status,
+-      .config_init            = qca808x_config_init,
+-      .soft_reset             = qca808x_soft_reset,
+-      .cable_test_start       = qca808x_cable_test_start,
+-      .cable_test_get_status  = qca808x_cable_test_get_status,
+-      .link_change_notify     = qca808x_link_change_notify,
+-      .led_brightness_set     = qca808x_led_brightness_set,
+-      .led_blink_set          = qca808x_led_blink_set,
+-      .led_hw_is_supported    = qca808x_led_hw_is_supported,
+-      .led_hw_control_set     = qca808x_led_hw_control_set,
+-      .led_hw_control_get     = qca808x_led_hw_control_get,
+-      .led_polarity_set       = qca808x_led_polarity_set,
+ }, };
+ module_phy_driver(at803x_driver);
+@@ -2028,7 +1134,6 @@ 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(QCA9561_PHY_ID) },
+-      { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
+       { }
+ };
+--- /dev/null
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -0,0 +1,934 @@
++// SPDX-License-Identifier: GPL-2.0+
++
++#include <linux/phy.h>
++#include <linux/module.h>
++#include <linux/ethtool_netlink.h>
++
++#include "qcom.h"
++
++/* ADC threshold */
++#define QCA808X_PHY_DEBUG_ADC_THRESHOLD               0x2c80
++#define QCA808X_ADC_THRESHOLD_MASK            GENMASK(7, 0)
++#define QCA808X_ADC_THRESHOLD_80MV            0
++#define QCA808X_ADC_THRESHOLD_100MV           0xf0
++#define QCA808X_ADC_THRESHOLD_200MV           0x0f
++#define QCA808X_ADC_THRESHOLD_300MV           0xff
++
++/* CLD control */
++#define QCA808X_PHY_MMD3_ADDR_CLD_CTRL7               0x8007
++#define QCA808X_8023AZ_AFE_CTRL_MASK          GENMASK(8, 4)
++#define QCA808X_8023AZ_AFE_EN                 0x90
++
++/* AZ control */
++#define QCA808X_PHY_MMD3_AZ_TRAINING_CTRL     0x8008
++#define QCA808X_MMD3_AZ_TRAINING_VAL          0x1c32
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB   0x8014
++#define QCA808X_MSE_THRESHOLD_20DB_VALUE      0x529
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB   0x800E
++#define QCA808X_MSE_THRESHOLD_17DB_VALUE      0x341
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB   0x801E
++#define QCA808X_MSE_THRESHOLD_27DB_VALUE      0x419
++
++#define QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB   0x8020
++#define QCA808X_MSE_THRESHOLD_28DB_VALUE      0x341
++
++#define QCA808X_PHY_MMD7_TOP_OPTION1          0x901c
++#define QCA808X_TOP_OPTION1_DATA              0x0
++
++#define QCA808X_PHY_MMD3_DEBUG_1              0xa100
++#define QCA808X_MMD3_DEBUG_1_VALUE            0x9203
++#define QCA808X_PHY_MMD3_DEBUG_2              0xa101
++#define QCA808X_MMD3_DEBUG_2_VALUE            0x48ad
++#define QCA808X_PHY_MMD3_DEBUG_3              0xa103
++#define QCA808X_MMD3_DEBUG_3_VALUE            0x1698
++#define QCA808X_PHY_MMD3_DEBUG_4              0xa105
++#define QCA808X_MMD3_DEBUG_4_VALUE            0x8001
++#define QCA808X_PHY_MMD3_DEBUG_5              0xa106
++#define QCA808X_MMD3_DEBUG_5_VALUE            0x1111
++#define QCA808X_PHY_MMD3_DEBUG_6              0xa011
++#define QCA808X_MMD3_DEBUG_6_VALUE            0x5f85
++
++/* master/slave seed config */
++#define QCA808X_PHY_DEBUG_LOCAL_SEED          9
++#define QCA808X_MASTER_SLAVE_SEED_ENABLE      BIT(1)
++#define QCA808X_MASTER_SLAVE_SEED_CFG         GENMASK(12, 2)
++#define QCA808X_MASTER_SLAVE_SEED_RANGE               0x32
++
++/* Hibernation yields lower power consumpiton in contrast with normal operation mode.
++ * when the copper cable is unplugged, the PHY enters into hibernation mode in about 10s.
++ */
++#define QCA808X_DBG_AN_TEST                   0xb
++#define QCA808X_HIBERNATION_EN                        BIT(15)
++
++#define QCA808X_CDT_ENABLE_TEST                       BIT(15)
++#define QCA808X_CDT_INTER_CHECK_DIS           BIT(13)
++#define QCA808X_CDT_STATUS                    BIT(11)
++#define QCA808X_CDT_LENGTH_UNIT                       BIT(10)
++
++#define QCA808X_MMD3_CDT_STATUS                       0x8064
++#define QCA808X_MMD3_CDT_DIAG_PAIR_A          0x8065
++#define QCA808X_MMD3_CDT_DIAG_PAIR_B          0x8066
++#define QCA808X_MMD3_CDT_DIAG_PAIR_C          0x8067
++#define QCA808X_MMD3_CDT_DIAG_PAIR_D          0x8068
++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT    GENMASK(15, 8)
++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT   GENMASK(7, 0)
++
++#define QCA808X_CDT_CODE_PAIR_A                       GENMASK(15, 12)
++#define QCA808X_CDT_CODE_PAIR_B                       GENMASK(11, 8)
++#define QCA808X_CDT_CODE_PAIR_C                       GENMASK(7, 4)
++#define QCA808X_CDT_CODE_PAIR_D                       GENMASK(3, 0)
++
++#define QCA808X_CDT_STATUS_STAT_TYPE          GENMASK(1, 0)
++#define QCA808X_CDT_STATUS_STAT_FAIL          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
++#define QCA808X_CDT_STATUS_STAT_NORMAL                FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN     FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT    FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
++
++#define QCA808X_CDT_STATUS_STAT_MDI           GENMASK(3, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI1          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
++#define QCA808X_CDT_STATUS_STAT_MDI2          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI3          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
++
++/* NORMAL are MDI with type set to 0 */
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI1
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI2
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI3
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
++
++/* Added for reference of existence but should be handled by wait_for_completion already */
++#define QCA808X_CDT_STATUS_STAT_BUSY          (BIT(1) | BIT(3))
++
++#define QCA808X_MMD7_LED_GLOBAL                       0x8073
++#define QCA808X_LED_BLINK_1                   GENMASK(11, 6)
++#define QCA808X_LED_BLINK_2                   GENMASK(5, 0)
++/* Values are the same for both BLINK_1 and BLINK_2 */
++#define QCA808X_LED_BLINK_FREQ_MASK           GENMASK(5, 3)
++#define QCA808X_LED_BLINK_FREQ_2HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
++#define QCA808X_LED_BLINK_FREQ_4HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
++#define QCA808X_LED_BLINK_FREQ_8HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
++#define QCA808X_LED_BLINK_FREQ_16HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
++#define QCA808X_LED_BLINK_FREQ_32HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
++#define QCA808X_LED_BLINK_FREQ_64HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
++#define QCA808X_LED_BLINK_FREQ_128HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
++#define QCA808X_LED_BLINK_FREQ_256HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
++#define QCA808X_LED_BLINK_DUTY_MASK           GENMASK(2, 0)
++#define QCA808X_LED_BLINK_DUTY_50_50          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
++#define QCA808X_LED_BLINK_DUTY_75_25          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
++#define QCA808X_LED_BLINK_DUTY_25_75          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
++#define QCA808X_LED_BLINK_DUTY_33_67          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
++#define QCA808X_LED_BLINK_DUTY_67_33          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
++#define QCA808X_LED_BLINK_DUTY_17_83          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
++#define QCA808X_LED_BLINK_DUTY_83_17          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
++#define QCA808X_LED_BLINK_DUTY_8_92           FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
++
++#define QCA808X_MMD7_LED2_CTRL                        0x8074
++#define QCA808X_MMD7_LED2_FORCE_CTRL          0x8075
++#define QCA808X_MMD7_LED1_CTRL                        0x8076
++#define QCA808X_MMD7_LED1_FORCE_CTRL          0x8077
++#define QCA808X_MMD7_LED0_CTRL                        0x8078
++#define QCA808X_MMD7_LED_CTRL(x)              (0x8078 - ((x) * 2))
++
++/* LED hw control pattern is the same for every LED */
++#define QCA808X_LED_PATTERN_MASK              GENMASK(15, 0)
++#define QCA808X_LED_SPEED2500_ON              BIT(15)
++#define QCA808X_LED_SPEED2500_BLINK           BIT(14)
++/* Follow blink trigger even if duplex or speed condition doesn't match */
++#define QCA808X_LED_BLINK_CHECK_BYPASS                BIT(13)
++#define QCA808X_LED_FULL_DUPLEX_ON            BIT(12)
++#define QCA808X_LED_HALF_DUPLEX_ON            BIT(11)
++#define QCA808X_LED_TX_BLINK                  BIT(10)
++#define QCA808X_LED_RX_BLINK                  BIT(9)
++#define QCA808X_LED_TX_ON_10MS                        BIT(8)
++#define QCA808X_LED_RX_ON_10MS                        BIT(7)
++#define QCA808X_LED_SPEED1000_ON              BIT(6)
++#define QCA808X_LED_SPEED100_ON                       BIT(5)
++#define QCA808X_LED_SPEED10_ON                        BIT(4)
++#define QCA808X_LED_COLLISION_BLINK           BIT(3)
++#define QCA808X_LED_SPEED1000_BLINK           BIT(2)
++#define QCA808X_LED_SPEED100_BLINK            BIT(1)
++#define QCA808X_LED_SPEED10_BLINK             BIT(0)
++
++#define QCA808X_MMD7_LED0_FORCE_CTRL          0x8079
++#define QCA808X_MMD7_LED_FORCE_CTRL(x)                (0x8079 - ((x) * 2))
++
++/* LED force ctrl is the same for every LED
++ * No documentation exist for this, not even internal one
++ * with NDA as QCOM gives only info about configuring
++ * hw control pattern rules and doesn't indicate any way
++ * to force the LED to specific mode.
++ * These define comes from reverse and testing and maybe
++ * lack of some info or some info are not entirely correct.
++ * For the basic LED control and hw control these finding
++ * are enough to support LED control in all the required APIs.
++ *
++ * On doing some comparison with implementation with qca807x,
++ * it was found that it's 1:1 equal to it and confirms all the
++ * reverse done. It was also found further specification with the
++ * force mode and the blink modes.
++ */
++#define QCA808X_LED_FORCE_EN                  BIT(15)
++#define QCA808X_LED_FORCE_MODE_MASK           GENMASK(14, 13)
++#define QCA808X_LED_FORCE_BLINK_1             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
++#define QCA808X_LED_FORCE_BLINK_2             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
++#define QCA808X_LED_FORCE_ON                  FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
++#define QCA808X_LED_FORCE_OFF                 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
++
++#define QCA808X_MMD7_LED_POLARITY_CTRL                0x901a
++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
++ * LED to active high. It's not clear what BIT 3 and BIT 4 does.
++ */
++#define QCA808X_LED_ACTIVE_HIGH                       BIT(6)
++
++/* QCA808X 1G chip type */
++#define QCA808X_PHY_MMD7_CHIP_TYPE            0x901d
++#define QCA808X_PHY_CHIP_TYPE_1G              BIT(0)
++
++#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL     0x9072
++#define QCA8081_PHY_FIFO_RSTN                 BIT(11)
++
++#define QCA8081_PHY_ID                                0x004dd101
++
++MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
++MODULE_AUTHOR("Matus Ujhelyi");
++MODULE_LICENSE("GPL");
++
++struct qca808x_priv {
++      int led_polarity_mode;
++};
++
++static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
++{
++      int ret;
++
++      /* Enable fast retrain */
++      ret = genphy_c45_fast_retrain(phydev, true);
++      if (ret)
++              return ret;
++
++      phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
++                    QCA808X_TOP_OPTION1_DATA);
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
++                    QCA808X_MSE_THRESHOLD_20DB_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
++                    QCA808X_MSE_THRESHOLD_17DB_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
++                    QCA808X_MSE_THRESHOLD_27DB_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
++                    QCA808X_MSE_THRESHOLD_28DB_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
++                    QCA808X_MMD3_DEBUG_1_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
++                    QCA808X_MMD3_DEBUG_4_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
++                    QCA808X_MMD3_DEBUG_5_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
++                    QCA808X_MMD3_DEBUG_3_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
++                    QCA808X_MMD3_DEBUG_6_VALUE);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
++                    QCA808X_MMD3_DEBUG_2_VALUE);
++
++      return 0;
++}
++
++static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
++{
++      u16 seed_value;
++
++      if (!enable)
++              return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
++                              QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
++
++      seed_value = get_random_u32_below(QCA808X_MASTER_SLAVE_SEED_RANGE);
++      return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
++                      QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
++                      FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
++                      QCA808X_MASTER_SLAVE_SEED_ENABLE);
++}
++
++static bool qca808x_is_prefer_master(struct phy_device *phydev)
++{
++      return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
++              (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
++}
++
++static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
++{
++      return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
++}
++
++static int qca808x_probe(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      struct qca808x_priv *priv;
++
++      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      /* Init LED polarity mode to -1 */
++      priv->led_polarity_mode = -1;
++
++      phydev->priv = priv;
++
++      return 0;
++}
++
++static int qca808x_config_init(struct phy_device *phydev)
++{
++      int ret;
++
++      /* Active adc&vga on 802.3az for the link 1000M and 100M */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
++                           QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
++      if (ret)
++              return ret;
++
++      /* Adjust the threshold on 802.3az for the link 1000M */
++      ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
++                          QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
++                          QCA808X_MMD3_AZ_TRAINING_VAL);
++      if (ret)
++              return ret;
++
++      if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
++              /* Config the fast retrain for the link 2500M */
++              ret = qca808x_phy_fast_retrain_config(phydev);
++              if (ret)
++                      return ret;
++
++              ret = genphy_read_master_slave(phydev);
++              if (ret < 0)
++                      return ret;
++
++              if (!qca808x_is_prefer_master(phydev)) {
++                      /* Enable seed and configure lower ramdom seed to make phy
++                       * linked as slave mode.
++                       */
++                      ret = qca808x_phy_ms_seed_enable(phydev, true);
++                      if (ret)
++                              return ret;
++              }
++      }
++
++      /* Configure adc threshold as 100mv for the link 10M */
++      return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
++                                   QCA808X_ADC_THRESHOLD_MASK,
++                                   QCA808X_ADC_THRESHOLD_100MV);
++}
++
++static int qca808x_read_status(struct phy_device *phydev)
++{
++      struct at803x_ss_mask ss_mask = { 0 };
++      int ret;
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
++      if (ret < 0)
++              return ret;
++
++      linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
++                       ret & MDIO_AN_10GBT_STAT_LP2_5G);
++
++      ret = genphy_read_status(phydev);
++      if (ret)
++              return ret;
++
++      /* qca8081 takes the different bits for speed value from at803x */
++      ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
++      ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
++      ret = at803x_read_specific_status(phydev, ss_mask);
++      if (ret < 0)
++              return ret;
++
++      if (phydev->link) {
++              if (phydev->speed == SPEED_2500)
++                      phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
++              else
++                      phydev->interface = PHY_INTERFACE_MODE_SGMII;
++      } else {
++              /* generate seed as a lower random value to make PHY linked as SLAVE easily,
++               * except for master/slave configuration fault detected or the master mode
++               * preferred.
++               *
++               * the reason for not putting this code into the function link_change_notify is
++               * the corner case where the link partner is also the qca8081 PHY and the seed
++               * value is configured as the same value, the link can't be up and no link change
++               * occurs.
++               */
++              if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
++                      if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
++                          qca808x_is_prefer_master(phydev)) {
++                              qca808x_phy_ms_seed_enable(phydev, false);
++                      } else {
++                              qca808x_phy_ms_seed_enable(phydev, true);
++                      }
++              }
++      }
++
++      return 0;
++}
++
++static int qca808x_soft_reset(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = genphy_soft_reset(phydev);
++      if (ret < 0)
++              return ret;
++
++      if (qca808x_has_fast_retrain_or_slave_seed(phydev))
++              ret = qca808x_phy_ms_seed_enable(phydev, true);
++
++      return ret;
++}
++
++static bool qca808x_cdt_fault_length_valid(int cdt_code)
++{
++      switch (cdt_code) {
++      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++              return true;
++      default:
++              return false;
++      }
++}
++
++static int qca808x_cable_test_result_trans(int cdt_code)
++{
++      switch (cdt_code) {
++      case QCA808X_CDT_STATUS_STAT_NORMAL:
++              return ETHTOOL_A_CABLE_RESULT_CODE_OK;
++      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++              return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
++      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++              return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++              return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
++      case QCA808X_CDT_STATUS_STAT_FAIL:
++      default:
++              return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
++      }
++}
++
++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
++                                  int result)
++{
++      int val;
++      u32 cdt_length_reg = 0;
++
++      switch (pair) {
++      case ETHTOOL_A_CABLE_PAIR_A:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
++              break;
++      case ETHTOOL_A_CABLE_PAIR_B:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
++              break;
++      case ETHTOOL_A_CABLE_PAIR_C:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
++              break;
++      case ETHTOOL_A_CABLE_PAIR_D:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
++      if (val < 0)
++              return val;
++
++      if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
++              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
++      else
++              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
++
++      return at803x_cdt_fault_length(val);
++}
++
++static int qca808x_cable_test_start(struct phy_device *phydev)
++{
++      int ret;
++
++      /* perform CDT with the following configs:
++       * 1. disable hibernation.
++       * 2. force PHY working in MDI mode.
++       * 3. for PHY working in 1000BaseT.
++       * 4. configure the threshold.
++       */
++
++      ret = at803x_debug_reg_mask(phydev, QCA808X_DBG_AN_TEST, QCA808X_HIBERNATION_EN, 0);
++      if (ret < 0)
++              return ret;
++
++      ret = at803x_config_mdix(phydev, ETH_TP_MDI);
++      if (ret < 0)
++              return ret;
++
++      /* Force 1000base-T needs to configure PMA/PMD and MII_BMCR */
++      phydev->duplex = DUPLEX_FULL;
++      phydev->speed = SPEED_1000;
++      ret = genphy_c45_pma_setup_forced(phydev);
++      if (ret < 0)
++              return ret;
++
++      ret = genphy_setup_forced(phydev);
++      if (ret < 0)
++              return ret;
++
++      /* configure the thresholds for open, short, pair ok test */
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8074, 0xc040);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8076, 0xc040);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8077, 0xa060);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8078, 0xc050);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
++
++      return 0;
++}
++
++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
++                                            u16 status)
++{
++      int length, result;
++      u16 pair_code;
++
++      switch (pair) {
++      case ETHTOOL_A_CABLE_PAIR_A:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_B:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_C:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_D:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      result = qca808x_cable_test_result_trans(pair_code);
++      ethnl_cable_test_result(phydev, pair, result);
++
++      if (qca808x_cdt_fault_length_valid(pair_code)) {
++              length = qca808x_cdt_fault_length(phydev, pair, result);
++              ethnl_cable_test_fault_length(phydev, pair, length);
++      }
++
++      return 0;
++}
++
++static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
++{
++      int ret, val;
++
++      *finished = false;
++
++      val = QCA808X_CDT_ENABLE_TEST |
++            QCA808X_CDT_LENGTH_UNIT;
++      ret = at803x_cdt_start(phydev, val);
++      if (ret)
++              return ret;
++
++      ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
++      if (ret)
++              return ret;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
++      if (val < 0)
++              return val;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
++      if (ret)
++              return ret;
++
++      *finished = true;
++
++      return 0;
++}
++
++static int qca808x_get_features(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = genphy_c45_pma_read_abilities(phydev);
++      if (ret)
++              return ret;
++
++      /* The autoneg ability is not existed in bit3 of MMD7.1,
++       * but it is supported by qca808x PHY, so we add it here
++       * manually.
++       */
++      linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
++
++      /* As for the qca8081 1G version chip, the 2500baseT ability is also
++       * existed in the bit0 of MMD1.21, we need to remove it manually if
++       * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
++       */
++      ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
++      if (ret < 0)
++              return ret;
++
++      if (QCA808X_PHY_CHIP_TYPE_1G & ret)
++              linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
++
++      return 0;
++}
++
++static int qca808x_config_aneg(struct phy_device *phydev)
++{
++      int phy_ctrl = 0;
++      int ret;
++
++      ret = at803x_prepare_config_aneg(phydev);
++      if (ret)
++              return ret;
++
++      /* The reg MII_BMCR also needs to be configured for force mode, the
++       * genphy_config_aneg is also needed.
++       */
++      if (phydev->autoneg == AUTONEG_DISABLE)
++              genphy_c45_pma_setup_forced(phydev);
++
++      if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
++              phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
++
++      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
++                                   MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
++      if (ret < 0)
++              return ret;
++
++      return __genphy_config_aneg(phydev, ret);
++}
++
++static void qca808x_link_change_notify(struct phy_device *phydev)
++{
++      /* Assert interface sgmii fifo on link down, deassert it on link up,
++       * the interface device address is always phy address added by 1.
++       */
++      mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
++                                 MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
++                                 QCA8081_PHY_FIFO_RSTN,
++                                 phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
++}
++
++static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
++                                  u16 *offload_trigger)
++{
++      /* Parsing specific to netdev trigger */
++      if (test_bit(TRIGGER_NETDEV_TX, &rules))
++              *offload_trigger |= QCA808X_LED_TX_BLINK;
++      if (test_bit(TRIGGER_NETDEV_RX, &rules))
++              *offload_trigger |= QCA808X_LED_RX_BLINK;
++      if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED10_ON;
++      if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED100_ON;
++      if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED1000_ON;
++      if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
++              *offload_trigger |= QCA808X_LED_SPEED2500_ON;
++      if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
++              *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
++      if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
++              *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
++
++      if (rules && !*offload_trigger)
++              return -EOPNOTSUPP;
++
++      /* Enable BLINK_CHECK_BYPASS by default to make the LED
++       * blink even with duplex or speed mode not enabled.
++       */
++      *offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
++
++      return 0;
++}
++
++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++                                QCA808X_LED_FORCE_EN);
++}
++
++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                     unsigned long rules)
++{
++      u16 offload_trigger = 0;
++
++      if (index > 2)
++              return -EINVAL;
++
++      return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
++}
++
++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                    unsigned long rules)
++{
++      u16 reg, offload_trigger = 0;
++      int ret;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_CTRL(index);
++
++      ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
++      if (ret)
++              return ret;
++
++      ret = qca808x_led_hw_control_enable(phydev, index);
++      if (ret)
++              return ret;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                            QCA808X_LED_PATTERN_MASK,
++                            offload_trigger);
++}
++
++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++      int val;
++
++      if (index > 2)
++              return false;
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++
++      return !(val & QCA808X_LED_FORCE_EN);
++}
++
++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                    unsigned long *rules)
++{
++      u16 reg;
++      int val;
++
++      if (index > 2)
++              return -EINVAL;
++
++      /* Check if we have hw control enabled */
++      if (qca808x_led_hw_control_status(phydev, index))
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_CTRL(index);
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++      if (val & QCA808X_LED_TX_BLINK)
++              set_bit(TRIGGER_NETDEV_TX, rules);
++      if (val & QCA808X_LED_RX_BLINK)
++              set_bit(TRIGGER_NETDEV_RX, rules);
++      if (val & QCA808X_LED_SPEED10_ON)
++              set_bit(TRIGGER_NETDEV_LINK_10, rules);
++      if (val & QCA808X_LED_SPEED100_ON)
++              set_bit(TRIGGER_NETDEV_LINK_100, rules);
++      if (val & QCA808X_LED_SPEED1000_ON)
++              set_bit(TRIGGER_NETDEV_LINK_1000, rules);
++      if (val & QCA808X_LED_SPEED2500_ON)
++              set_bit(TRIGGER_NETDEV_LINK_2500, rules);
++      if (val & QCA808X_LED_HALF_DUPLEX_ON)
++              set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
++      if (val & QCA808X_LED_FULL_DUPLEX_ON)
++              set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
++
++      return 0;
++}
++
++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_CTRL(index);
++
++      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++                                QCA808X_LED_PATTERN_MASK);
++}
++
++static int qca808x_led_brightness_set(struct phy_device *phydev,
++                                    u8 index, enum led_brightness value)
++{
++      u16 reg;
++      int ret;
++
++      if (index > 2)
++              return -EINVAL;
++
++      if (!value) {
++              ret = qca808x_led_hw_control_reset(phydev, index);
++              if (ret)
++                      return ret;
++      }
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                            QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++                            QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
++                                                           QCA808X_LED_FORCE_OFF);
++}
++
++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
++                               unsigned long *delay_on,
++                               unsigned long *delay_off)
++{
++      int ret;
++      u16 reg;
++
++      if (index > 2)
++              return -EINVAL;
++
++      reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++      /* Set blink to 50% off, 50% on at 4Hz by default */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
++                           QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
++                           QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
++      if (ret)
++              return ret;
++
++      /* We use BLINK_1 for normal blinking */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
++      if (ret)
++              return ret;
++
++      /* We set blink to 4Hz, aka 250ms */
++      *delay_on = 250 / 2;
++      *delay_off = 250 / 2;
++
++      return 0;
++}
++
++static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
++                                  unsigned long modes)
++{
++      struct qca808x_priv *priv = phydev->priv;
++      bool active_low = false;
++      u32 mode;
++
++      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++              switch (mode) {
++              case PHY_LED_ACTIVE_LOW:
++                      active_low = true;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      /* PHY polarity is global and can't be set per LED.
++       * To detect this, check if last requested polarity mode
++       * match the new one.
++       */
++      if (priv->led_polarity_mode >= 0 &&
++          priv->led_polarity_mode != active_low) {
++              phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
++              return -EINVAL;
++      }
++
++      /* Save the last PHY polarity mode */
++      priv->led_polarity_mode = active_low;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN,
++                            QCA808X_MMD7_LED_POLARITY_CTRL,
++                            QCA808X_LED_ACTIVE_HIGH,
++                            active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
++}
++
++static struct phy_driver qca808x_driver[] = {
++{
++      /* Qualcomm QCA8081 */
++      PHY_ID_MATCH_EXACT(QCA8081_PHY_ID),
++      .name                   = "Qualcomm QCA8081",
++      .flags                  = PHY_POLL_CABLE_TEST,
++      .probe                  = qca808x_probe,
++      .config_intr            = at803x_config_intr,
++      .handle_interrupt       = at803x_handle_interrupt,
++      .get_tunable            = at803x_get_tunable,
++      .set_tunable            = at803x_set_tunable,
++      .set_wol                = at803x_set_wol,
++      .get_wol                = at803x_get_wol,
++      .get_features           = qca808x_get_features,
++      .config_aneg            = qca808x_config_aneg,
++      .suspend                = genphy_suspend,
++      .resume                 = genphy_resume,
++      .read_status            = qca808x_read_status,
++      .config_init            = qca808x_config_init,
++      .soft_reset             = qca808x_soft_reset,
++      .cable_test_start       = qca808x_cable_test_start,
++      .cable_test_get_status  = qca808x_cable_test_get_status,
++      .link_change_notify     = qca808x_link_change_notify,
++      .led_brightness_set     = qca808x_led_brightness_set,
++      .led_blink_set          = qca808x_led_blink_set,
++      .led_hw_is_supported    = qca808x_led_hw_is_supported,
++      .led_hw_control_set     = qca808x_led_hw_control_set,
++      .led_hw_control_get     = qca808x_led_hw_control_get,
++      .led_polarity_set       = qca808x_led_polarity_set,
++}, };
++
++module_phy_driver(qca808x_driver);
++
++static struct mdio_device_id __maybe_unused qca808x_tbl[] = {
++      { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
++      { }
++};
++
++MODULE_DEVICE_TABLE(mdio, qca808x_tbl);
diff --git a/target/linux/generic/backport-6.6/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch b/target/linux/generic/backport-6.6/714-v6.8-01-net-phy-make-addr-type-u8-in-phy_package_shared-stru.patch
new file mode 100644 (file)
index 0000000..4562592
--- /dev/null
@@ -0,0 +1,28 @@
+From ebb30ccbbdbd6fae5177b676da4f4ac92bb4f635 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 15 Dec 2023 14:15:31 +0100
+Subject: [PATCH 1/4] net: phy: make addr type u8 in phy_package_shared struct
+
+Switch addr type in phy_package_shared struct to u8.
+
+The value is already checked to be non negative and to be less than
+PHY_MAX_ADDR, hence u8 is better suited than using int.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ include/linux/phy.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -338,7 +338,7 @@ struct mdio_bus_stats {
+  * phy_package_leave().
+  */
+ struct phy_package_shared {
+-      int addr;
++      u8 addr;
+       refcount_t refcnt;
+       unsigned long flags;
+       size_t priv_size;
diff --git a/target/linux/generic/backport-6.6/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch b/target/linux/generic/backport-6.6/714-v6.8-02-net-phy-extend-PHY-package-API-to-support-multiple-g.patch
new file mode 100644 (file)
index 0000000..68f8552
--- /dev/null
@@ -0,0 +1,341 @@
+From 9eea577eb1155fe4a183bc5e7bf269b0b2e7a6ba Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 15 Dec 2023 14:15:32 +0100
+Subject: [PATCH 2/4] net: phy: extend PHY package API to support multiple
+ global address
+
+Current API for PHY package are limited to single address to configure
+global settings for the PHY package.
+
+It was found that some PHY package (for example the qca807x, a PHY
+package that is shipped with a bundle of 5 PHY) requires multiple PHY
+address to configure global settings. An example scenario is a PHY that
+have a dedicated PHY for PSGMII/serdes calibrarion and have a specific
+PHY in the package where the global PHY mode is set and affects every
+other PHY in the package.
+
+Change the API in the following way:
+- Change phy_package_join() to take the base addr of the PHY package
+  instead of the global PHY addr.
+- Make __/phy_package_write/read() require an additional arg that
+  select what global PHY address to use by passing the offset from the
+  base addr passed on phy_package_join().
+
+Each user of this API is updated to follow this new implementation
+following a pattern where an enum is defined to declare the offset of the
+addr.
+
+We also drop the check if shared is defined as any user of the
+phy_package_read/write is expected to use phy_package_join first. Misuse
+of this will correctly trigger a kernel panic for NULL pointer
+exception.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/bcm54140.c       | 16 ++++++--
+ drivers/net/phy/mscc/mscc.h      |  5 +++
+ drivers/net/phy/mscc/mscc_main.c |  4 +-
+ drivers/net/phy/phy_device.c     | 35 +++++++++--------
+ include/linux/phy.h              | 64 +++++++++++++++++++++-----------
+ 5 files changed, 80 insertions(+), 44 deletions(-)
+
+--- a/drivers/net/phy/bcm54140.c
++++ b/drivers/net/phy/bcm54140.c
+@@ -128,6 +128,10 @@
+ #define BCM54140_DEFAULT_DOWNSHIFT 5
+ #define BCM54140_MAX_DOWNSHIFT 9
++enum bcm54140_global_phy {
++      BCM54140_BASE_ADDR = 0,
++};
++
+ struct bcm54140_priv {
+       int port;
+       int base_addr;
+@@ -429,11 +433,13 @@ static int bcm54140_base_read_rdb(struct
+       int ret;
+       phy_lock_mdio_bus(phydev);
+-      ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
++      ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
++                                MII_BCM54XX_RDB_ADDR, rdb);
+       if (ret < 0)
+               goto out;
+-      ret = __phy_package_read(phydev, MII_BCM54XX_RDB_DATA);
++      ret = __phy_package_read(phydev, BCM54140_BASE_ADDR,
++                               MII_BCM54XX_RDB_DATA);
+ out:
+       phy_unlock_mdio_bus(phydev);
+@@ -446,11 +452,13 @@ static int bcm54140_base_write_rdb(struc
+       int ret;
+       phy_lock_mdio_bus(phydev);
+-      ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
++      ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
++                                MII_BCM54XX_RDB_ADDR, rdb);
+       if (ret < 0)
+               goto out;
+-      ret = __phy_package_write(phydev, MII_BCM54XX_RDB_DATA, val);
++      ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
++                                MII_BCM54XX_RDB_DATA, val);
+ out:
+       phy_unlock_mdio_bus(phydev);
+--- a/drivers/net/phy/mscc/mscc.h
++++ b/drivers/net/phy/mscc/mscc.h
+@@ -416,6 +416,11 @@ struct vsc8531_private {
+  * gpio_lock: used for PHC operations. Common for all PHYs as the load/save GPIO
+  * is shared.
+  */
++
++enum vsc85xx_global_phy {
++      VSC88XX_BASE_ADDR = 0,
++};
++
+ struct vsc85xx_shared_private {
+       struct mutex gpio_lock;
+ };
+--- a/drivers/net/phy/mscc/mscc_main.c
++++ b/drivers/net/phy/mscc/mscc_main.c
+@@ -711,7 +711,7 @@ int phy_base_write(struct phy_device *ph
+               dump_stack();
+       }
+-      return __phy_package_write(phydev, regnum, val);
++      return __phy_package_write(phydev, VSC88XX_BASE_ADDR, regnum, val);
+ }
+ /* phydev->bus->mdio_lock should be locked when using this function */
+@@ -722,7 +722,7 @@ int phy_base_read(struct phy_device *phy
+               dump_stack();
+       }
+-      return __phy_package_read(phydev, regnum);
++      return __phy_package_read(phydev, VSC88XX_BASE_ADDR, regnum);
+ }
+ u32 vsc85xx_csr_read(struct phy_device *phydev,
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1658,20 +1658,22 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1
+ /**
+  * phy_package_join - join a common PHY group
+  * @phydev: target phy_device struct
+- * @addr: cookie and PHY address for global register access
++ * @base_addr: cookie and base PHY address of PHY package for offset
++ *   calculation of global register access
+  * @priv_size: if non-zero allocate this amount of bytes for private data
+  *
+  * This joins a PHY group and provides a shared storage for all phydevs in
+  * this group. This is intended to be used for packages which contain
+  * more than one PHY, for example a quad PHY transceiver.
+  *
+- * The addr parameter serves as a cookie which has to have the same value
+- * for all members of one group and as a PHY address to access generic
+- * registers of a PHY package. Usually, one of the PHY addresses of the
+- * different PHYs in the package provides access to these global registers.
++ * The base_addr parameter serves as cookie which has to have the same values
++ * for all members of one group and as the base PHY address of the PHY package
++ * for offset calculation to access generic registers of a PHY package.
++ * Usually, one of the PHY addresses of the different PHYs in the package
++ * provides access to these global registers.
+  * The address which is given here, will be used in the phy_package_read()
+- * and phy_package_write() convenience functions. If your PHY doesn't have
+- * global registers you can just pick any of the PHY addresses.
++ * and phy_package_write() convenience functions as base and added to the
++ * passed offset in those functions.
+  *
+  * This will set the shared pointer of the phydev to the shared storage.
+  * If this is the first call for a this cookie the shared storage will be
+@@ -1681,17 +1683,17 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1
+  * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
+  * with the same cookie but a different priv_size is an error.
+  */
+-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size)
++int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size)
+ {
+       struct mii_bus *bus = phydev->mdio.bus;
+       struct phy_package_shared *shared;
+       int ret;
+-      if (addr < 0 || addr >= PHY_MAX_ADDR)
++      if (base_addr < 0 || base_addr >= PHY_MAX_ADDR)
+               return -EINVAL;
+       mutex_lock(&bus->shared_lock);
+-      shared = bus->shared[addr];
++      shared = bus->shared[base_addr];
+       if (!shared) {
+               ret = -ENOMEM;
+               shared = kzalloc(sizeof(*shared), GFP_KERNEL);
+@@ -1703,9 +1705,9 @@ int phy_package_join(struct phy_device *
+                               goto err_free;
+                       shared->priv_size = priv_size;
+               }
+-              shared->addr = addr;
++              shared->base_addr = base_addr;
+               refcount_set(&shared->refcnt, 1);
+-              bus->shared[addr] = shared;
++              bus->shared[base_addr] = shared;
+       } else {
+               ret = -EINVAL;
+               if (priv_size && priv_size != shared->priv_size)
+@@ -1743,7 +1745,7 @@ void phy_package_leave(struct phy_device
+               return;
+       if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
+-              bus->shared[shared->addr] = NULL;
++              bus->shared[shared->base_addr] = NULL;
+               mutex_unlock(&bus->shared_lock);
+               kfree(shared->priv);
+               kfree(shared);
+@@ -1762,7 +1764,8 @@ static void devm_phy_package_leave(struc
+  * devm_phy_package_join - resource managed phy_package_join()
+  * @dev: device that is registering this PHY package
+  * @phydev: target phy_device struct
+- * @addr: cookie and PHY address for global register access
++ * @base_addr: cookie and base PHY address of PHY package for offset
++ *   calculation of global register access
+  * @priv_size: if non-zero allocate this amount of bytes for private data
+  *
+  * Managed phy_package_join(). Shared storage fetched by this function,
+@@ -1770,7 +1773,7 @@ static void devm_phy_package_leave(struc
+  * phy_package_join() for more information.
+  */
+ int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+-                        int addr, size_t priv_size)
++                        int base_addr, size_t priv_size)
+ {
+       struct phy_device **ptr;
+       int ret;
+@@ -1780,7 +1783,7 @@ int devm_phy_package_join(struct device
+       if (!ptr)
+               return -ENOMEM;
+-      ret = phy_package_join(phydev, addr, priv_size);
++      ret = phy_package_join(phydev, base_addr, priv_size);
+       if (!ret) {
+               *ptr = phydev;
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -327,7 +327,8 @@ struct mdio_bus_stats {
+ /**
+  * struct phy_package_shared - Shared information in PHY packages
+- * @addr: Common PHY address used to combine PHYs in one package
++ * @base_addr: Base PHY address of PHY package used to combine PHYs
++ *   in one package and for offset calculation of phy_package_read/write
+  * @refcnt: Number of PHYs connected to this shared data
+  * @flags: Initialization of PHY package
+  * @priv_size: Size of the shared private data @priv
+@@ -338,7 +339,7 @@ struct mdio_bus_stats {
+  * phy_package_leave().
+  */
+ struct phy_package_shared {
+-      u8 addr;
++      u8 base_addr;
+       refcount_t refcnt;
+       unsigned long flags;
+       size_t priv_size;
+@@ -1969,10 +1970,10 @@ int phy_ethtool_get_link_ksettings(struc
+ int phy_ethtool_set_link_ksettings(struct net_device *ndev,
+                                  const struct ethtool_link_ksettings *cmd);
+ int phy_ethtool_nway_reset(struct net_device *ndev);
+-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size);
++int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size);
+ void phy_package_leave(struct phy_device *phydev);
+ int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+-                        int addr, size_t priv_size);
++                        int base_addr, size_t priv_size);
+ int __init mdio_bus_init(void);
+ void mdio_bus_exit(void);
+@@ -1995,46 +1996,65 @@ int __phy_hwtstamp_set(struct phy_device
+                      struct kernel_hwtstamp_config *config,
+                      struct netlink_ext_ack *extack);
+-static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
++static inline int phy_package_address(struct phy_device *phydev,
++                                    unsigned int addr_offset)
+ {
+       struct phy_package_shared *shared = phydev->shared;
++      u8 base_addr = shared->base_addr;
+-      if (!shared)
++      if (addr_offset >= PHY_MAX_ADDR - base_addr)
+               return -EIO;
+-      return mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
++      /* we know that addr will be in the range 0..31 and thus the
++       * implicit cast to a signed int is not a problem.
++       */
++      return base_addr + addr_offset;
+ }
+-static inline int __phy_package_read(struct phy_device *phydev, u32 regnum)
++static inline int phy_package_read(struct phy_device *phydev,
++                                 unsigned int addr_offset, u32 regnum)
+ {
+-      struct phy_package_shared *shared = phydev->shared;
++      int addr = phy_package_address(phydev, addr_offset);
+-      if (!shared)
+-              return -EIO;
++      if (addr < 0)
++              return addr;
++
++      return mdiobus_read(phydev->mdio.bus, addr, regnum);
++}
++
++static inline int __phy_package_read(struct phy_device *phydev,
++                                   unsigned int addr_offset, u32 regnum)
++{
++      int addr = phy_package_address(phydev, addr_offset);
++
++      if (addr < 0)
++              return addr;
+-      return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
++      return __mdiobus_read(phydev->mdio.bus, addr, regnum);
+ }
+ static inline int phy_package_write(struct phy_device *phydev,
+-                                  u32 regnum, u16 val)
++                                  unsigned int addr_offset, u32 regnum,
++                                  u16 val)
+ {
+-      struct phy_package_shared *shared = phydev->shared;
++      int addr = phy_package_address(phydev, addr_offset);
+-      if (!shared)
+-              return -EIO;
++      if (addr < 0)
++              return addr;
+-      return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
++      return mdiobus_write(phydev->mdio.bus, addr, regnum, val);
+ }
+ static inline int __phy_package_write(struct phy_device *phydev,
+-                                    u32 regnum, u16 val)
++                                    unsigned int addr_offset, u32 regnum,
++                                    u16 val)
+ {
+-      struct phy_package_shared *shared = phydev->shared;
++      int addr = phy_package_address(phydev, addr_offset);
+-      if (!shared)
+-              return -EIO;
++      if (addr < 0)
++              return addr;
+-      return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
++      return __mdiobus_write(phydev->mdio.bus, addr, regnum, val);
+ }
+ static inline bool __phy_package_set_once(struct phy_device *phydev,
diff --git a/target/linux/generic/backport-6.6/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch b/target/linux/generic/backport-6.6/714-v6.8-03-net-phy-restructure-__phy_write-read_mmd-to-helper-a.patch
new file mode 100644 (file)
index 0000000..f0ca35a
--- /dev/null
@@ -0,0 +1,116 @@
+From 028672bd1d73cf65249a420c1de75e8d2acd2f6a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 15 Dec 2023 14:15:33 +0100
+Subject: [PATCH 3/4] net: phy: restructure __phy_write/read_mmd to helper and
+ phydev user
+
+Restructure phy_write_mmd and phy_read_mmd to implement generic helper
+for direct mdiobus access for mmd and use these helper for phydev user.
+
+This is needed in preparation of PHY package API that requires generic
+access to the mdiobus and are deatched from phydev struct but instead
+access them based on PHY package base_addr and offsets.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/phy-core.c | 64 ++++++++++++++++++--------------------
+ 1 file changed, 30 insertions(+), 34 deletions(-)
+
+--- a/drivers/net/phy/phy-core.c
++++ b/drivers/net/phy/phy-core.c
+@@ -540,6 +540,28 @@ static void mmd_phy_indirect(struct mii_
+                       devad | MII_MMD_CTRL_NOINCR);
+ }
++static int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,
++                      int devad, u32 regnum)
++{
++      if (is_c45)
++              return __mdiobus_c45_read(bus, phy_addr, devad, regnum);
++
++      mmd_phy_indirect(bus, phy_addr, devad, regnum);
++      /* Read the content of the MMD's selected register */
++      return __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
++}
++
++static int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,
++                       int devad, u32 regnum, u16 val)
++{
++      if (is_c45)
++              return __mdiobus_c45_write(bus, phy_addr, devad, regnum, val);
++
++      mmd_phy_indirect(bus, phy_addr, devad, regnum);
++      /* Write the data into MMD's selected register */
++      return __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
++}
++
+ /**
+  * __phy_read_mmd - Convenience function for reading a register
+  * from an MMD on a given PHY.
+@@ -551,26 +573,14 @@ static void mmd_phy_indirect(struct mii_
+  */
+ int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
+ {
+-      int val;
+-
+       if (regnum > (u16)~0 || devad > 32)
+               return -EINVAL;
+-      if (phydev->drv && phydev->drv->read_mmd) {
+-              val = phydev->drv->read_mmd(phydev, devad, regnum);
+-      } else if (phydev->is_c45) {
+-              val = __mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
+-                                       devad, regnum);
+-      } else {
+-              struct mii_bus *bus = phydev->mdio.bus;
+-              int phy_addr = phydev->mdio.addr;
+-
+-              mmd_phy_indirect(bus, phy_addr, devad, regnum);
+-
+-              /* Read the content of the MMD's selected register */
+-              val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
+-      }
+-      return val;
++      if (phydev->drv && phydev->drv->read_mmd)
++              return phydev->drv->read_mmd(phydev, devad, regnum);
++
++      return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr,
++                          phydev->is_c45, devad, regnum);
+ }
+ EXPORT_SYMBOL(__phy_read_mmd);
+@@ -607,28 +617,14 @@ EXPORT_SYMBOL(phy_read_mmd);
+  */
+ int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
+ {
+-      int ret;
+-
+       if (regnum > (u16)~0 || devad > 32)
+               return -EINVAL;
+-      if (phydev->drv && phydev->drv->write_mmd) {
+-              ret = phydev->drv->write_mmd(phydev, devad, regnum, val);
+-      } else if (phydev->is_c45) {
+-              ret = __mdiobus_c45_write(phydev->mdio.bus, phydev->mdio.addr,
+-                                        devad, regnum, val);
+-      } else {
+-              struct mii_bus *bus = phydev->mdio.bus;
+-              int phy_addr = phydev->mdio.addr;
++      if (phydev->drv && phydev->drv->write_mmd)
++              return phydev->drv->write_mmd(phydev, devad, regnum, val);
+-              mmd_phy_indirect(bus, phy_addr, devad, regnum);
+-
+-              /* Write the data into MMD's selected register */
+-              __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
+-
+-              ret = 0;
+-      }
+-      return ret;
++      return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr,
++                           phydev->is_c45, devad, regnum, val);
+ }
+ EXPORT_SYMBOL(__phy_write_mmd);
diff --git a/target/linux/generic/backport-6.6/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch b/target/linux/generic/backport-6.6/714-v6.8-04-net-phy-add-support-for-PHY-package-MMD-read-write.patch
new file mode 100644 (file)
index 0000000..c20d2ec
--- /dev/null
@@ -0,0 +1,196 @@
+From d63710fc0f1a501fd75a7025e3070a96ffa1645f Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 15 Dec 2023 14:15:34 +0100
+Subject: [PATCH 4/4] net: phy: add support for PHY package MMD read/write
+
+Some PHY in PHY package may require to read/write MMD regs to correctly
+configure the PHY package.
+
+Add support for these additional required function in both lock and no
+lock variant.
+
+It's assumed that the entire PHY package is either C22 or C45. We use
+C22 or C45 way of writing/reading to mmd regs based on the passed phydev
+whether it's C22 or C45.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/phy-core.c | 140 +++++++++++++++++++++++++++++++++++++
+ include/linux/phy.h        |  16 +++++
+ 2 files changed, 156 insertions(+)
+
+--- a/drivers/net/phy/phy-core.c
++++ b/drivers/net/phy/phy-core.c
+@@ -651,6 +651,146 @@ int phy_write_mmd(struct phy_device *phy
+ EXPORT_SYMBOL(phy_write_mmd);
+ /**
++ * __phy_package_read_mmd - read MMD reg relative to PHY package base addr
++ * @phydev: The phy_device struct
++ * @addr_offset: The offset to be added to PHY package base_addr
++ * @devad: The MMD to read from
++ * @regnum: The register on the MMD to read
++ *
++ * Convenience helper for reading a register of an MMD on a given PHY
++ * using the PHY package base address. The base address is added to
++ * the addr_offset value.
++ *
++ * Same calling rules as for __phy_read();
++ *
++ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
++ */
++int __phy_package_read_mmd(struct phy_device *phydev,
++                         unsigned int addr_offset, int devad,
++                         u32 regnum)
++{
++      int addr = phy_package_address(phydev, addr_offset);
++
++      if (addr < 0)
++              return addr;
++
++      if (regnum > (u16)~0 || devad > 32)
++              return -EINVAL;
++
++      return mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad,
++                          regnum);
++}
++EXPORT_SYMBOL(__phy_package_read_mmd);
++
++/**
++ * phy_package_read_mmd - read MMD reg relative to PHY package base addr
++ * @phydev: The phy_device struct
++ * @addr_offset: The offset to be added to PHY package base_addr
++ * @devad: The MMD to read from
++ * @regnum: The register on the MMD to read
++ *
++ * Convenience helper for reading a register of an MMD on a given PHY
++ * using the PHY package base address. The base address is added to
++ * the addr_offset value.
++ *
++ * Same calling rules as for phy_read();
++ *
++ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
++ */
++int phy_package_read_mmd(struct phy_device *phydev,
++                       unsigned int addr_offset, int devad,
++                       u32 regnum)
++{
++      int addr = phy_package_address(phydev, addr_offset);
++      int val;
++
++      if (addr < 0)
++              return addr;
++
++      if (regnum > (u16)~0 || devad > 32)
++              return -EINVAL;
++
++      phy_lock_mdio_bus(phydev);
++      val = mmd_phy_read(phydev->mdio.bus, addr, phydev->is_c45, devad,
++                         regnum);
++      phy_unlock_mdio_bus(phydev);
++
++      return val;
++}
++EXPORT_SYMBOL(phy_package_read_mmd);
++
++/**
++ * __phy_package_write_mmd - write MMD reg relative to PHY package base addr
++ * @phydev: The phy_device struct
++ * @addr_offset: The offset to be added to PHY package base_addr
++ * @devad: The MMD to write to
++ * @regnum: The register on the MMD to write
++ * @val: value to write to @regnum
++ *
++ * Convenience helper for writing a register of an MMD on a given PHY
++ * using the PHY package base address. The base address is added to
++ * the addr_offset value.
++ *
++ * Same calling rules as for __phy_write();
++ *
++ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
++ */
++int __phy_package_write_mmd(struct phy_device *phydev,
++                          unsigned int addr_offset, int devad,
++                          u32 regnum, u16 val)
++{
++      int addr = phy_package_address(phydev, addr_offset);
++
++      if (addr < 0)
++              return addr;
++
++      if (regnum > (u16)~0 || devad > 32)
++              return -EINVAL;
++
++      return mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad,
++                           regnum, val);
++}
++EXPORT_SYMBOL(__phy_package_write_mmd);
++
++/**
++ * phy_package_write_mmd - write MMD reg relative to PHY package base addr
++ * @phydev: The phy_device struct
++ * @addr_offset: The offset to be added to PHY package base_addr
++ * @devad: The MMD to write to
++ * @regnum: The register on the MMD to write
++ * @val: value to write to @regnum
++ *
++ * Convenience helper for writing a register of an MMD on a given PHY
++ * using the PHY package base address. The base address is added to
++ * the addr_offset value.
++ *
++ * Same calling rules as for phy_write();
++ *
++ * NOTE: It's assumed that the entire PHY package is either C22 or C45.
++ */
++int phy_package_write_mmd(struct phy_device *phydev,
++                        unsigned int addr_offset, int devad,
++                        u32 regnum, u16 val)
++{
++      int addr = phy_package_address(phydev, addr_offset);
++      int ret;
++
++      if (addr < 0)
++              return addr;
++
++      if (regnum > (u16)~0 || devad > 32)
++              return -EINVAL;
++
++      phy_lock_mdio_bus(phydev);
++      ret = mmd_phy_write(phydev->mdio.bus, addr, phydev->is_c45, devad,
++                          regnum, val);
++      phy_unlock_mdio_bus(phydev);
++
++      return ret;
++}
++EXPORT_SYMBOL(phy_package_write_mmd);
++
++/**
+  * phy_modify_changed - Function for modifying a PHY register
+  * @phydev: the phy_device struct
+  * @regnum: register number to modify
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -2057,6 +2057,22 @@ static inline int __phy_package_write(st
+       return __mdiobus_write(phydev->mdio.bus, addr, regnum, val);
+ }
++int __phy_package_read_mmd(struct phy_device *phydev,
++                         unsigned int addr_offset, int devad,
++                         u32 regnum);
++
++int phy_package_read_mmd(struct phy_device *phydev,
++                       unsigned int addr_offset, int devad,
++                       u32 regnum);
++
++int __phy_package_write_mmd(struct phy_device *phydev,
++                          unsigned int addr_offset, int devad,
++                          u32 regnum, u16 val);
++
++int phy_package_write_mmd(struct phy_device *phydev,
++                        unsigned int addr_offset, int devad,
++                        u32 regnum, u16 val);
++
+ static inline bool __phy_package_set_once(struct phy_device *phydev,
+                                         unsigned int b)
+ {
diff --git a/target/linux/generic/backport-6.6/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch b/target/linux/generic/backport-6.6/715-v6.9-01-net-phy-qcom-qca808x-fix-logic-error-in-LED-brightne.patch
new file mode 100644 (file)
index 0000000..0a7b535
--- /dev/null
@@ -0,0 +1,36 @@
+From f2ec98566775dd4341ec1dcf93aa5859c60de826 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 1 Feb 2024 14:46:00 +0100
+Subject: [PATCH 1/2] net: phy: qcom: qca808x: fix logic error in LED
+ brightness set
+
+In switching to using phy_modify_mmd and a more short version of the
+LED ON/OFF condition in later revision, it was made a logic error where
+
+value ? QCA808X_LED_FORCE_ON : QCA808X_LED_FORCE_OFF is always true as
+value is always OR with QCA808X_LED_FORCE_EN due to missing ()
+resulting in the testing condition being QCA808X_LED_FORCE_EN | value.
+
+Add the () to apply the correct condition and restore correct
+functionality of the brightness ON/OFF.
+
+Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -820,8 +820,8 @@ static int qca808x_led_brightness_set(st
+       return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+                             QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
+-                            QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
+-                                                           QCA808X_LED_FORCE_OFF);
++                            QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
++                                                           QCA808X_LED_FORCE_OFF));
+ }
+ static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
diff --git a/target/linux/generic/backport-6.6/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch b/target/linux/generic/backport-6.6/715-v6.9-02-net-phy-qcom-qca808x-default-to-LED-active-High-if-n.patch
new file mode 100644 (file)
index 0000000..e32ed7f
--- /dev/null
@@ -0,0 +1,41 @@
+From f203c8c77c7616c099647636f4c67d59a45fe8a2 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 1 Feb 2024 14:46:01 +0100
+Subject: [PATCH 2/2] net: phy: qcom: qca808x: default to LED active High if
+ not set
+
+qca808x PHY provide support for the led_polarity_set OP to configure
+and apply the active-low property but on PHY reset, the Active High bit
+is not set resulting in the LED driven as active-low.
+
+To fix this, check if active-low is not set in DT and enable Active High
+polarity by default to restore correct funcionality of the LED.
+
+Fixes: 7196062b64ee ("net: phy: at803x: add LED support for qca808x")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -290,8 +290,18 @@ static int qca808x_probe(struct phy_devi
+ static int qca808x_config_init(struct phy_device *phydev)
+ {
++      struct qca808x_priv *priv = phydev->priv;
+       int ret;
++      /* Default to LED Active High if active-low not in DT */
++      if (priv->led_polarity_mode == -1) {
++              ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN,
++                                     QCA808X_MMD7_LED_POLARITY_CTRL,
++                                     QCA808X_LED_ACTIVE_HIGH);
++              if (ret)
++                      return ret;
++      }
++
+       /* Active adc&vga on 802.3az for the link 1000M and 100M */
+       ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
+                            QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
diff --git a/target/linux/generic/backport-6.6/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch b/target/linux/generic/backport-6.6/716-v6.9-02-net-phy-add-support-for-scanning-PHY-in-PHY-packages.patch
new file mode 100644 (file)
index 0000000..81f2d1d
--- /dev/null
@@ -0,0 +1,211 @@
+From 385ef48f468696d6d172eb367656a3466fa0408d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 6 Feb 2024 18:31:05 +0100
+Subject: [PATCH 02/10] net: phy: add support for scanning PHY in PHY packages
+ nodes
+
+Add support for scanning PHY in PHY package nodes. PHY packages nodes
+are just container for actual PHY on the MDIO bus.
+
+Their PHY address defined in the PHY package node are absolute and
+reflect the address on the MDIO bus.
+
+mdio_bus.c and of_mdio.c is updated to now support and parse also
+PHY package subnode by checking if the node name match
+"ethernet-phy-package".
+
+As PHY package reg is mandatory and each PHY in the PHY package must
+have a reg, every invalid PHY Package node is ignored and will be
+skipped by the autoscan fallback.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/mdio/of_mdio.c | 79 +++++++++++++++++++++++++++-----------
+ drivers/net/phy/mdio_bus.c | 44 +++++++++++++++++----
+ 2 files changed, 92 insertions(+), 31 deletions(-)
+
+--- a/drivers/net/mdio/of_mdio.c
++++ b/drivers/net/mdio/of_mdio.c
+@@ -138,6 +138,53 @@ bool of_mdiobus_child_is_phy(struct devi
+ }
+ EXPORT_SYMBOL(of_mdiobus_child_is_phy);
++static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np,
++                                 bool *scanphys)
++{
++      struct device_node *child;
++      int addr, rc = 0;
++
++      /* Loop over the child nodes and register a phy_device for each phy */
++      for_each_available_child_of_node(np, child) {
++              if (of_node_name_eq(child, "ethernet-phy-package")) {
++                      /* Ignore invalid ethernet-phy-package node */
++                      if (!of_find_property(child, "reg", NULL))
++                              continue;
++
++                      rc = __of_mdiobus_parse_phys(mdio, child, NULL);
++                      if (rc && rc != -ENODEV)
++                              goto exit;
++
++                      continue;
++              }
++
++              addr = of_mdio_parse_addr(&mdio->dev, child);
++              if (addr < 0) {
++                      /* Skip scanning for invalid ethernet-phy-package node */
++                      if (scanphys)
++                              *scanphys = true;
++                      continue;
++              }
++
++              if (of_mdiobus_child_is_phy(child))
++                      rc = of_mdiobus_register_phy(mdio, child, addr);
++              else
++                      rc = of_mdiobus_register_device(mdio, child, addr);
++
++              if (rc == -ENODEV)
++                      dev_err(&mdio->dev,
++                              "MDIO device at address %d is missing.\n",
++                              addr);
++              else if (rc)
++                      goto exit;
++      }
++
++      return 0;
++exit:
++      of_node_put(child);
++      return rc;
++}
++
+ /**
+  * __of_mdiobus_register - Register mii_bus and create PHYs from the device tree
+  * @mdio: pointer to mii_bus structure
+@@ -179,33 +226,18 @@ int __of_mdiobus_register(struct mii_bus
+               return rc;
+       /* Loop over the child nodes and register a phy_device for each phy */
+-      for_each_available_child_of_node(np, child) {
+-              addr = of_mdio_parse_addr(&mdio->dev, child);
+-              if (addr < 0) {
+-                      scanphys = true;
+-                      continue;
+-              }
+-
+-              if (of_mdiobus_child_is_phy(child))
+-                      rc = of_mdiobus_register_phy(mdio, child, addr);
+-              else
+-                      rc = of_mdiobus_register_device(mdio, child, addr);
+-
+-              if (rc == -ENODEV)
+-                      dev_err(&mdio->dev,
+-                              "MDIO device at address %d is missing.\n",
+-                              addr);
+-              else if (rc)
+-                      goto unregister;
+-      }
++      rc = __of_mdiobus_parse_phys(mdio, np, &scanphys);
++      if (rc)
++              goto unregister;
+       if (!scanphys)
+               return 0;
+       /* auto scan for PHYs with empty reg property */
+       for_each_available_child_of_node(np, child) {
+-              /* Skip PHYs with reg property set */
+-              if (of_property_present(child, "reg"))
++              /* Skip PHYs with reg property set or ethernet-phy-package node */
++              if (of_property_present(child, "reg") ||
++                  of_node_name_eq(child, "ethernet-phy-package"))
+                       continue;
+               for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
+@@ -226,15 +258,16 @@ int __of_mdiobus_register(struct mii_bus
+                               if (!rc)
+                                       break;
+                               if (rc != -ENODEV)
+-                                      goto unregister;
++                                      goto put_unregister;
+                       }
+               }
+       }
+       return 0;
+-unregister:
++put_unregister:
+       of_node_put(child);
++unregister:
+       mdiobus_unregister(mdio);
+       return rc;
+ }
+--- a/drivers/net/phy/mdio_bus.c
++++ b/drivers/net/phy/mdio_bus.c
+@@ -455,19 +455,34 @@ EXPORT_SYMBOL(of_mdio_find_bus);
+  * found, set the of_node pointer for the mdio device. This allows
+  * auto-probed phy devices to be supplied with information passed in
+  * via DT.
++ * If a PHY package is found, PHY is searched also there.
+  */
+-static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
+-                                  struct mdio_device *mdiodev)
++static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev,
++                             struct device_node *np)
+ {
+-      struct device *dev = &mdiodev->dev;
+       struct device_node *child;
+-      if (dev->of_node || !bus->dev.of_node)
+-              return;
+-
+-      for_each_available_child_of_node(bus->dev.of_node, child) {
++      for_each_available_child_of_node(np, child) {
+               int addr;
++              if (of_node_name_eq(child, "ethernet-phy-package")) {
++                      /* Validate PHY package reg presence */
++                      if (!of_find_property(child, "reg", NULL)) {
++                              of_node_put(child);
++                              return -EINVAL;
++                      }
++
++                      if (!of_mdiobus_find_phy(dev, mdiodev, child)) {
++                              /* The refcount for the PHY package will be
++                               * incremented later when PHY join the Package.
++                               */
++                              of_node_put(child);
++                              return 0;
++                      }
++
++                      continue;
++              }
++
+               addr = of_mdio_parse_addr(dev, child);
+               if (addr < 0)
+                       continue;
+@@ -477,9 +492,22 @@ static void of_mdiobus_link_mdiodev(stru
+                       /* The refcount on "child" is passed to the mdio
+                        * device. Do _not_ use of_node_put(child) here.
+                        */
+-                      return;
++                      return 0;
+               }
+       }
++
++      return -ENODEV;
++}
++
++static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
++                                  struct mdio_device *mdiodev)
++{
++      struct device *dev = &mdiodev->dev;
++
++      if (dev->of_node || !bus->dev.of_node)
++              return;
++
++      of_mdiobus_find_phy(dev, mdiodev, bus->dev.of_node);
+ }
+ #else /* !IS_ENABLED(CONFIG_OF_MDIO) */
+ static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio,
diff --git a/target/linux/generic/backport-6.6/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch b/target/linux/generic/backport-6.6/716-v6.9-03-net-phy-add-devm-of_phy_package_join-helper.patch
new file mode 100644 (file)
index 0000000..25f6c5c
--- /dev/null
@@ -0,0 +1,185 @@
+From 471e8fd3afcef5a9f9089f0bd21965ad9ba35c91 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 6 Feb 2024 18:31:06 +0100
+Subject: [PATCH 03/10] net: phy: add devm/of_phy_package_join helper
+
+Add devm/of_phy_package_join helper to join PHYs in a PHY package. These
+are variant of the manual phy_package_join with the difference that
+these will use DT nodes to derive the base_addr instead of manually
+passing an hardcoded value.
+
+An additional value is added in phy_package_shared, "np" to reference
+the PHY package node pointer in specific PHY driver probe_once and
+config_init_once functions to make use of additional specific properties
+defined in the PHY package node in DT.
+
+The np value is filled only with of_phy_package_join if a valid PHY
+package node is found. A valid PHY package node must have the node name
+set to "ethernet-phy-package".
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/phy_device.c | 96 ++++++++++++++++++++++++++++++++++++
+ include/linux/phy.h          |  6 +++
+ 2 files changed, 102 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1706,6 +1706,7 @@ int phy_package_join(struct phy_device *
+                       shared->priv_size = priv_size;
+               }
+               shared->base_addr = base_addr;
++              shared->np = NULL;
+               refcount_set(&shared->refcnt, 1);
+               bus->shared[base_addr] = shared;
+       } else {
+@@ -1729,6 +1730,63 @@ err_unlock:
+ EXPORT_SYMBOL_GPL(phy_package_join);
+ /**
++ * of_phy_package_join - join a common PHY group in PHY package
++ * @phydev: target phy_device struct
++ * @priv_size: if non-zero allocate this amount of bytes for private data
++ *
++ * This is a variant of phy_package_join for PHY package defined in DT.
++ *
++ * The parent node of the @phydev is checked as a valid PHY package node
++ * structure (by matching the node name "ethernet-phy-package") and the
++ * base_addr for the PHY package is passed to phy_package_join.
++ *
++ * With this configuration the shared struct will also have the np value
++ * filled to use additional DT defined properties in PHY specific
++ * probe_once and config_init_once PHY package OPs.
++ *
++ * Returns < 0 on error, 0 on success. Esp. calling phy_package_join()
++ * with the same cookie but a different priv_size is an error. Or a parent
++ * node is not detected or is not valid or doesn't match the expected node
++ * name for PHY package.
++ */
++int of_phy_package_join(struct phy_device *phydev, size_t priv_size)
++{
++      struct device_node *node = phydev->mdio.dev.of_node;
++      struct device_node *package_node;
++      u32 base_addr;
++      int ret;
++
++      if (!node)
++              return -EINVAL;
++
++      package_node = of_get_parent(node);
++      if (!package_node)
++              return -EINVAL;
++
++      if (!of_node_name_eq(package_node, "ethernet-phy-package")) {
++              ret = -EINVAL;
++              goto exit;
++      }
++
++      if (of_property_read_u32(package_node, "reg", &base_addr)) {
++              ret = -EINVAL;
++              goto exit;
++      }
++
++      ret = phy_package_join(phydev, base_addr, priv_size);
++      if (ret)
++              goto exit;
++
++      phydev->shared->np = package_node;
++
++      return 0;
++exit:
++      of_node_put(package_node);
++      return ret;
++}
++EXPORT_SYMBOL_GPL(of_phy_package_join);
++
++/**
+  * phy_package_leave - leave a common PHY group
+  * @phydev: target phy_device struct
+  *
+@@ -1744,6 +1802,10 @@ void phy_package_leave(struct phy_device
+       if (!shared)
+               return;
++      /* Decrease the node refcount on leave if present */
++      if (shared->np)
++              of_node_put(shared->np);
++
+       if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
+               bus->shared[shared->base_addr] = NULL;
+               mutex_unlock(&bus->shared_lock);
+@@ -1797,6 +1859,40 @@ int devm_phy_package_join(struct device
+ EXPORT_SYMBOL_GPL(devm_phy_package_join);
+ /**
++ * devm_of_phy_package_join - resource managed of_phy_package_join()
++ * @dev: device that is registering this PHY package
++ * @phydev: target phy_device struct
++ * @priv_size: if non-zero allocate this amount of bytes for private data
++ *
++ * Managed of_phy_package_join(). Shared storage fetched by this function,
++ * phy_package_leave() is automatically called on driver detach. See
++ * of_phy_package_join() for more information.
++ */
++int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
++                           size_t priv_size)
++{
++      struct phy_device **ptr;
++      int ret;
++
++      ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
++                         GFP_KERNEL);
++      if (!ptr)
++              return -ENOMEM;
++
++      ret = of_phy_package_join(phydev, priv_size);
++
++      if (!ret) {
++              *ptr = phydev;
++              devres_add(dev, ptr);
++      } else {
++              devres_free(ptr);
++      }
++
++      return ret;
++}
++EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
++
++/**
+  * phy_detach - detach a PHY device from its network device
+  * @phydev: target phy_device struct
+  *
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -329,6 +329,7 @@ struct mdio_bus_stats {
+  * struct phy_package_shared - Shared information in PHY packages
+  * @base_addr: Base PHY address of PHY package used to combine PHYs
+  *   in one package and for offset calculation of phy_package_read/write
++ * @np: Pointer to the Device Node if PHY package defined in DT
+  * @refcnt: Number of PHYs connected to this shared data
+  * @flags: Initialization of PHY package
+  * @priv_size: Size of the shared private data @priv
+@@ -340,6 +341,8 @@ struct mdio_bus_stats {
+  */
+ struct phy_package_shared {
+       u8 base_addr;
++      /* With PHY package defined in DT this points to the PHY package node */
++      struct device_node *np;
+       refcount_t refcnt;
+       unsigned long flags;
+       size_t priv_size;
+@@ -1971,9 +1974,12 @@ int phy_ethtool_set_link_ksettings(struc
+                                  const struct ethtool_link_ksettings *cmd);
+ int phy_ethtool_nway_reset(struct net_device *ndev);
+ int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size);
++int of_phy_package_join(struct phy_device *phydev, size_t priv_size);
+ void phy_package_leave(struct phy_device *phydev);
+ int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+                         int base_addr, size_t priv_size);
++int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
++                           size_t priv_size);
+ int __init mdio_bus_init(void);
+ void mdio_bus_exit(void);
diff --git a/target/linux/generic/backport-6.6/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch b/target/linux/generic/backport-6.6/716-v6.9-04-net-phy-qcom-move-more-function-to-shared-library.patch
new file mode 100644 (file)
index 0000000..e935725
--- /dev/null
@@ -0,0 +1,583 @@
+From 737eb75a815f9c08dcbb6631db57f4f4b0540a5b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 6 Feb 2024 18:31:07 +0100
+Subject: [PATCH 04/10] net: phy: qcom: move more function to shared library
+
+Move more function to shared library in preparation for introduction of
+new PHY Family qca807x that will make use of both functions from at803x
+and qca808x as it's a transition PHY with some implementation of at803x
+and some from the new qca808x.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/at803x.c       |  35 -----
+ drivers/net/phy/qcom/qca808x.c      | 205 ----------------------------
+ drivers/net/phy/qcom/qcom-phy-lib.c | 193 ++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qcom.h         |  51 +++++++
+ 4 files changed, 244 insertions(+), 240 deletions(-)
+
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -504,41 +504,6 @@ static void at803x_link_change_notify(st
+       }
+ }
+-static int at803x_read_status(struct phy_device *phydev)
+-{
+-      struct at803x_ss_mask ss_mask = { 0 };
+-      int err, old_link = phydev->link;
+-
+-      /* Update the link, but return if there was an error */
+-      err = genphy_update_link(phydev);
+-      if (err)
+-              return err;
+-
+-      /* why bother the PHY if nothing can have changed */
+-      if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+-              return 0;
+-
+-      phydev->speed = SPEED_UNKNOWN;
+-      phydev->duplex = DUPLEX_UNKNOWN;
+-      phydev->pause = 0;
+-      phydev->asym_pause = 0;
+-
+-      err = genphy_read_lpa(phydev);
+-      if (err < 0)
+-              return err;
+-
+-      ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
+-      ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
+-      err = at803x_read_specific_status(phydev, ss_mask);
+-      if (err < 0)
+-              return err;
+-
+-      if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
+-              phy_resolve_aneg_pause(phydev);
+-
+-      return 0;
+-}
+-
+ static int at803x_config_aneg(struct phy_device *phydev)
+ {
+       struct at803x_priv *priv = phydev->priv;
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -2,7 +2,6 @@
+ #include <linux/phy.h>
+ #include <linux/module.h>
+-#include <linux/ethtool_netlink.h>
+ #include "qcom.h"
+@@ -63,55 +62,6 @@
+ #define QCA808X_DBG_AN_TEST                   0xb
+ #define QCA808X_HIBERNATION_EN                        BIT(15)
+-#define QCA808X_CDT_ENABLE_TEST                       BIT(15)
+-#define QCA808X_CDT_INTER_CHECK_DIS           BIT(13)
+-#define QCA808X_CDT_STATUS                    BIT(11)
+-#define QCA808X_CDT_LENGTH_UNIT                       BIT(10)
+-
+-#define QCA808X_MMD3_CDT_STATUS                       0x8064
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_A          0x8065
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_B          0x8066
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_C          0x8067
+-#define QCA808X_MMD3_CDT_DIAG_PAIR_D          0x8068
+-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT    GENMASK(15, 8)
+-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT   GENMASK(7, 0)
+-
+-#define QCA808X_CDT_CODE_PAIR_A                       GENMASK(15, 12)
+-#define QCA808X_CDT_CODE_PAIR_B                       GENMASK(11, 8)
+-#define QCA808X_CDT_CODE_PAIR_C                       GENMASK(7, 4)
+-#define QCA808X_CDT_CODE_PAIR_D                       GENMASK(3, 0)
+-
+-#define QCA808X_CDT_STATUS_STAT_TYPE          GENMASK(1, 0)
+-#define QCA808X_CDT_STATUS_STAT_FAIL          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
+-#define QCA808X_CDT_STATUS_STAT_NORMAL                FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
+-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN     FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
+-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT    FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
+-
+-#define QCA808X_CDT_STATUS_STAT_MDI           GENMASK(3, 2)
+-#define QCA808X_CDT_STATUS_STAT_MDI1          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
+-#define QCA808X_CDT_STATUS_STAT_MDI2          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
+-#define QCA808X_CDT_STATUS_STAT_MDI3          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
+-
+-/* NORMAL are MDI with type set to 0 */
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI1
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI2
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI3
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
+-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
+-                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
+-
+-/* Added for reference of existence but should be handled by wait_for_completion already */
+-#define QCA808X_CDT_STATUS_STAT_BUSY          (BIT(1) | BIT(3))
+-
+ #define QCA808X_MMD7_LED_GLOBAL                       0x8073
+ #define QCA808X_LED_BLINK_1                   GENMASK(11, 6)
+ #define QCA808X_LED_BLINK_2                   GENMASK(5, 0)
+@@ -406,86 +356,6 @@ static int qca808x_soft_reset(struct phy
+       return ret;
+ }
+-static bool qca808x_cdt_fault_length_valid(int cdt_code)
+-{
+-      switch (cdt_code) {
+-      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+-              return true;
+-      default:
+-              return false;
+-      }
+-}
+-
+-static int qca808x_cable_test_result_trans(int cdt_code)
+-{
+-      switch (cdt_code) {
+-      case QCA808X_CDT_STATUS_STAT_NORMAL:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+-      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+-      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
+-      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
+-      case QCA808X_CDT_STATUS_STAT_FAIL:
+-      default:
+-              return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+-      }
+-}
+-
+-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
+-                                  int result)
+-{
+-      int val;
+-      u32 cdt_length_reg = 0;
+-
+-      switch (pair) {
+-      case ETHTOOL_A_CABLE_PAIR_A:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_B:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_C:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_D:
+-              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
+-      if (val < 0)
+-              return val;
+-
+-      if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
+-              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
+-      else
+-              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
+-
+-      return at803x_cdt_fault_length(val);
+-}
+-
+ static int qca808x_cable_test_start(struct phy_device *phydev)
+ {
+       int ret;
+@@ -526,81 +396,6 @@ static int qca808x_cable_test_start(stru
+       return 0;
+ }
+-
+-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
+-                                            u16 status)
+-{
+-      int length, result;
+-      u16 pair_code;
+-
+-      switch (pair) {
+-      case ETHTOOL_A_CABLE_PAIR_A:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_B:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_C:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
+-              break;
+-      case ETHTOOL_A_CABLE_PAIR_D:
+-              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      result = qca808x_cable_test_result_trans(pair_code);
+-      ethnl_cable_test_result(phydev, pair, result);
+-
+-      if (qca808x_cdt_fault_length_valid(pair_code)) {
+-              length = qca808x_cdt_fault_length(phydev, pair, result);
+-              ethnl_cable_test_fault_length(phydev, pair, length);
+-      }
+-
+-      return 0;
+-}
+-
+-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
+-{
+-      int ret, val;
+-
+-      *finished = false;
+-
+-      val = QCA808X_CDT_ENABLE_TEST |
+-            QCA808X_CDT_LENGTH_UNIT;
+-      ret = at803x_cdt_start(phydev, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
+-      if (ret)
+-              return ret;
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
+-      if (val < 0)
+-              return val;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
+-      if (ret)
+-              return ret;
+-
+-      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
+-      if (ret)
+-              return ret;
+-
+-      *finished = true;
+-
+-      return 0;
+-}
+ static int qca808x_get_features(struct phy_device *phydev)
+ {
+--- a/drivers/net/phy/qcom/qcom-phy-lib.c
++++ b/drivers/net/phy/qcom/qcom-phy-lib.c
+@@ -5,6 +5,7 @@
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
++#include <linux/ethtool_netlink.h>
+ #include "qcom.h"
+@@ -311,6 +312,42 @@ int at803x_prepare_config_aneg(struct ph
+ }
+ EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
++int at803x_read_status(struct phy_device *phydev)
++{
++      struct at803x_ss_mask ss_mask = { 0 };
++      int err, old_link = phydev->link;
++
++      /* Update the link, but return if there was an error */
++      err = genphy_update_link(phydev);
++      if (err)
++              return err;
++
++      /* why bother the PHY if nothing can have changed */
++      if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
++              return 0;
++
++      phydev->speed = SPEED_UNKNOWN;
++      phydev->duplex = DUPLEX_UNKNOWN;
++      phydev->pause = 0;
++      phydev->asym_pause = 0;
++
++      err = genphy_read_lpa(phydev);
++      if (err < 0)
++              return err;
++
++      ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
++      ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
++      err = at803x_read_specific_status(phydev, ss_mask);
++      if (err < 0)
++              return err;
++
++      if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
++              phy_resolve_aneg_pause(phydev);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(at803x_read_status);
++
+ static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
+ {
+       int val;
+@@ -427,3 +464,159 @@ int at803x_cdt_wait_for_completion(struc
+       return ret < 0 ? ret : 0;
+ }
+ EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
++
++static bool qca808x_cdt_fault_length_valid(int cdt_code)
++{
++      switch (cdt_code) {
++      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++              return true;
++      default:
++              return false;
++      }
++}
++
++static int qca808x_cable_test_result_trans(int cdt_code)
++{
++      switch (cdt_code) {
++      case QCA808X_CDT_STATUS_STAT_NORMAL:
++              return ETHTOOL_A_CABLE_RESULT_CODE_OK;
++      case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
++              return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
++      case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
++              return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
++      case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
++              return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
++      case QCA808X_CDT_STATUS_STAT_FAIL:
++      default:
++              return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
++      }
++}
++
++static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
++                                  int result)
++{
++      int val;
++      u32 cdt_length_reg = 0;
++
++      switch (pair) {
++      case ETHTOOL_A_CABLE_PAIR_A:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
++              break;
++      case ETHTOOL_A_CABLE_PAIR_B:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
++              break;
++      case ETHTOOL_A_CABLE_PAIR_C:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
++              break;
++      case ETHTOOL_A_CABLE_PAIR_D:
++              cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
++      if (val < 0)
++              return val;
++
++      if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
++              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
++      else
++              val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
++
++      return at803x_cdt_fault_length(val);
++}
++
++static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
++                                            u16 status)
++{
++      int length, result;
++      u16 pair_code;
++
++      switch (pair) {
++      case ETHTOOL_A_CABLE_PAIR_A:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_B:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_C:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
++              break;
++      case ETHTOOL_A_CABLE_PAIR_D:
++              pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      result = qca808x_cable_test_result_trans(pair_code);
++      ethnl_cable_test_result(phydev, pair, result);
++
++      if (qca808x_cdt_fault_length_valid(pair_code)) {
++              length = qca808x_cdt_fault_length(phydev, pair, result);
++              ethnl_cable_test_fault_length(phydev, pair, length);
++      }
++
++      return 0;
++}
++
++int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
++{
++      int ret, val;
++
++      *finished = false;
++
++      val = QCA808X_CDT_ENABLE_TEST |
++            QCA808X_CDT_LENGTH_UNIT;
++      ret = at803x_cdt_start(phydev, val);
++      if (ret)
++              return ret;
++
++      ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
++      if (ret)
++              return ret;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
++      if (val < 0)
++              return val;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
++      if (ret)
++              return ret;
++
++      ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
++      if (ret)
++              return ret;
++
++      *finished = true;
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
+--- a/drivers/net/phy/qcom/qcom.h
++++ b/drivers/net/phy/qcom/qcom.h
+@@ -54,6 +54,55 @@
+ #define AT803X_CDT_STATUS_STAT_MASK           GENMASK(9, 8)
+ #define AT803X_CDT_STATUS_DELTA_TIME_MASK     GENMASK(7, 0)
++#define QCA808X_CDT_ENABLE_TEST                       BIT(15)
++#define QCA808X_CDT_INTER_CHECK_DIS           BIT(13)
++#define QCA808X_CDT_STATUS                    BIT(11)
++#define QCA808X_CDT_LENGTH_UNIT                       BIT(10)
++
++#define QCA808X_MMD3_CDT_STATUS                       0x8064
++#define QCA808X_MMD3_CDT_DIAG_PAIR_A          0x8065
++#define QCA808X_MMD3_CDT_DIAG_PAIR_B          0x8066
++#define QCA808X_MMD3_CDT_DIAG_PAIR_C          0x8067
++#define QCA808X_MMD3_CDT_DIAG_PAIR_D          0x8068
++#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT    GENMASK(15, 8)
++#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT   GENMASK(7, 0)
++
++#define QCA808X_CDT_CODE_PAIR_A                       GENMASK(15, 12)
++#define QCA808X_CDT_CODE_PAIR_B                       GENMASK(11, 8)
++#define QCA808X_CDT_CODE_PAIR_C                       GENMASK(7, 4)
++#define QCA808X_CDT_CODE_PAIR_D                       GENMASK(3, 0)
++
++#define QCA808X_CDT_STATUS_STAT_TYPE          GENMASK(1, 0)
++#define QCA808X_CDT_STATUS_STAT_FAIL          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
++#define QCA808X_CDT_STATUS_STAT_NORMAL                FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
++#define QCA808X_CDT_STATUS_STAT_SAME_OPEN     FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
++#define QCA808X_CDT_STATUS_STAT_SAME_SHORT    FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
++
++#define QCA808X_CDT_STATUS_STAT_MDI           GENMASK(3, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI1          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
++#define QCA808X_CDT_STATUS_STAT_MDI2          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
++#define QCA808X_CDT_STATUS_STAT_MDI3          FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
++
++/* NORMAL are MDI with type set to 0 */
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI1
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI1)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI2
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI2)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL     QCA808X_CDT_STATUS_STAT_MDI3
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN               (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
++#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT      (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
++                                                                       QCA808X_CDT_STATUS_STAT_MDI3)
++
++/* Added for reference of existence but should be handled by wait_for_completion already */
++#define QCA808X_CDT_STATUS_STAT_BUSY          (BIT(1) | BIT(3))
++
+ #define AT803X_LOC_MAC_ADDR_0_15_OFFSET               0x804C
+ #define AT803X_LOC_MAC_ADDR_16_31_OFFSET      0x804B
+ #define AT803X_LOC_MAC_ADDR_32_47_OFFSET      0x804A
+@@ -110,6 +159,7 @@ int at803x_read_specific_status(struct p
+                               struct at803x_ss_mask ss_mask);
+ int at803x_config_mdix(struct phy_device *phydev, u8 ctrl);
+ int at803x_prepare_config_aneg(struct phy_device *phydev);
++int at803x_read_status(struct phy_device *phydev);
+ int at803x_get_tunable(struct phy_device *phydev,
+                      struct ethtool_tunable *tuna, void *data);
+ int at803x_set_tunable(struct phy_device *phydev,
+@@ -118,3 +168,4 @@ int at803x_cdt_fault_length(int dt);
+ int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start);
+ int at803x_cdt_wait_for_completion(struct phy_device *phydev,
+                                  u32 cdt_en);
++int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
diff --git a/target/linux/generic/backport-6.6/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch b/target/linux/generic/backport-6.6/716-v6.9-06-net-phy-provide-whether-link-has-changed-in-c37_read.patch
new file mode 100644 (file)
index 0000000..a96b9f1
--- /dev/null
@@ -0,0 +1,100 @@
+From 9b1d5e055508393561e26bd1720f4c2639b03b1a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 6 Feb 2024 18:31:09 +0100
+Subject: [PATCH 06/10] net: phy: provide whether link has changed in
+ c37_read_status
+
+Some PHY driver might require additional regs call after
+genphy_c37_read_status() is called.
+
+Expand genphy_c37_read_status to provide a bool wheather the link has
+changed or not to permit PHY driver to skip additional regs call if
+nothing has changed.
+
+Every user of genphy_c37_read_status() is updated with the new
+additional bool.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/broadcom.c    |  3 ++-
+ drivers/net/phy/phy_device.c  | 11 +++++++++--
+ drivers/net/phy/qcom/at803x.c |  3 ++-
+ include/linux/phy.h           |  2 +-
+ 4 files changed, 14 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -665,10 +665,11 @@ static int bcm54616s_config_aneg(struct
+ static int bcm54616s_read_status(struct phy_device *phydev)
+ {
+       struct bcm54616s_phy_priv *priv = phydev->priv;
++      bool changed;
+       int err;
+       if (priv->mode_1000bx_en)
+-              err = genphy_c37_read_status(phydev);
++              err = genphy_c37_read_status(phydev, &changed);
+       else
+               err = genphy_read_status(phydev);
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -2615,12 +2615,15 @@ EXPORT_SYMBOL(genphy_read_status);
+ /**
+  * genphy_c37_read_status - check the link status and update current link state
+  * @phydev: target phy_device struct
++ * @changed: pointer where to store if link changed
+  *
+  * Description: Check the link, then figure out the current state
+  *   by comparing what we advertise with what the link partner
+  *   advertises. This function is for Clause 37 1000Base-X mode.
++ *
++ *   If link has changed, @changed is set to true, false otherwise.
+  */
+-int genphy_c37_read_status(struct phy_device *phydev)
++int genphy_c37_read_status(struct phy_device *phydev, bool *changed)
+ {
+       int lpa, err, old_link = phydev->link;
+@@ -2630,9 +2633,13 @@ int genphy_c37_read_status(struct phy_de
+               return err;
+       /* why bother the PHY if nothing can have changed */
+-      if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
++      if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) {
++              *changed = false;
+               return 0;
++      }
++      /* Signal link has changed */
++      *changed = true;
+       phydev->duplex = DUPLEX_UNKNOWN;
+       phydev->pause = 0;
+       phydev->asym_pause = 0;
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -912,9 +912,10 @@ static int at8031_config_intr(struct phy
+ static int at8031_read_status(struct phy_device *phydev)
+ {
+       struct at803x_priv *priv = phydev->priv;
++      bool changed;
+       if (priv->is_1000basex)
+-              return genphy_c37_read_status(phydev);
++              return genphy_c37_read_status(phydev, &changed);
+       return at803x_read_status(phydev);
+ }
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1849,7 +1849,7 @@ int genphy_write_mmd_unsupported(struct
+ /* Clause 37 */
+ int genphy_c37_config_aneg(struct phy_device *phydev);
+-int genphy_c37_read_status(struct phy_device *phydev);
++int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
+ /* Clause 45 PHY */
+ int genphy_c45_restart_aneg(struct phy_device *phydev);
diff --git a/target/linux/generic/backport-6.6/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch b/target/linux/generic/backport-6.6/716-v6.9-07-net-phy-qcom-add-support-for-QCA807x-PHY-Family.patch
new file mode 100644 (file)
index 0000000..bbf0f76
--- /dev/null
@@ -0,0 +1,668 @@
+From d1cb613efbd3cd7d0c000167816beb3f248f5eb8 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robert.marko@sartura.hr>
+Date: Tue, 6 Feb 2024 18:31:10 +0100
+Subject: [PATCH 07/10] net: phy: qcom: add support for QCA807x PHY Family
+
+This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s.
+
+They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te,
+100BASE-TX and 1000BASE-T PHY-s.
+
+They feature 2 SerDes, one for PSGMII or QSGMII connection with
+MAC, while second one is SGMII for connection to MAC or fiber.
+
+Both models have a combo port that supports 1000BASE-X and
+100BASE-FX fiber.
+
+PHY package can be configured in 3 mode following this table:
+
+              First Serdes mode       Second Serdes mode
+Option 1      PSGMII for copper       Disabled
+              ports 0-4
+Option 2      PSGMII for copper       1000BASE-X / 100BASE-FX
+              ports 0-4
+Option 3      QSGMII for copper       SGMII for
+              ports 0-3               copper port 4
+
+Each PHY inside of QCA807x series has 4 digitally controlled
+output only pins that natively drive LED-s.
+But some vendors used these to driver generic LED-s controlled
+by userspace, so lets enable registering each PHY as GPIO
+controller and add driver for it.
+
+These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x
+boards.
+
+Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Robert Marko <robert.marko@sartura.hr>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/Kconfig   |   8 +
+ drivers/net/phy/qcom/Makefile  |   1 +
+ drivers/net/phy/qcom/qca807x.c | 597 +++++++++++++++++++++++++++++++++
+ 3 files changed, 606 insertions(+)
+ create mode 100644 drivers/net/phy/qcom/qca807x.c
+
+--- a/drivers/net/phy/qcom/Kconfig
++++ b/drivers/net/phy/qcom/Kconfig
+@@ -20,3 +20,11 @@ config QCA808X_PHY
+       select QCOM_NET_PHYLIB
+       help
+         Currently supports the QCA8081 model
++
++config QCA807X_PHY
++      tristate "Qualcomm QCA807x PHYs"
++      select QCOM_NET_PHYLIB
++      depends on OF_MDIO
++      help
++        Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII
++        control PHY.
+--- a/drivers/net/phy/qcom/Makefile
++++ b/drivers/net/phy/qcom/Makefile
+@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_NET_PHYLIB)  += qcom-ph
+ obj-$(CONFIG_AT803X_PHY)      += at803x.o
+ obj-$(CONFIG_QCA83XX_PHY)     += qca83xx.o
+ obj-$(CONFIG_QCA808X_PHY)     += qca808x.o
++obj-$(CONFIG_QCA807X_PHY)     += qca807x.o
+--- /dev/null
++++ b/drivers/net/phy/qcom/qca807x.c
+@@ -0,0 +1,597 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2023 Sartura Ltd.
++ *
++ * Author: Robert Marko <robert.marko@sartura.hr>
++ *         Christian Marangi <ansuelsmth@gmail.com>
++ *
++ * Qualcomm QCA8072 and QCA8075 PHY driver
++ */
++
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/phy.h>
++#include <linux/bitfield.h>
++#include <linux/gpio/driver.h>
++#include <linux/sfp.h>
++
++#include "qcom.h"
++
++#define QCA807X_CHIP_CONFIGURATION                            0x1f
++#define QCA807X_BT_BX_REG_SEL                                 BIT(15)
++#define QCA807X_BT_BX_REG_SEL_FIBER                           0
++#define QCA807X_BT_BX_REG_SEL_COPPER                          1
++#define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK              GENMASK(3, 0)
++#define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII          4
++#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER          3
++#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER     0
++
++#define QCA807X_MEDIA_SELECT_STATUS                           0x1a
++#define QCA807X_MEDIA_DETECTED_COPPER                         BIT(5)
++#define QCA807X_MEDIA_DETECTED_1000_BASE_X                    BIT(4)
++#define QCA807X_MEDIA_DETECTED_100_BASE_FX                    BIT(3)
++
++#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION                        0x807e
++#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN             BIT(0)
++
++#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH   0x801a
++#define QCA807X_CONTROL_DAC_MASK                              GENMASK(2, 0)
++/* List of tweaks enabled by this bit:
++ * - With both FULL amplitude and FULL bias current: bias current
++ *   is set to half.
++ * - With only DSP amplitude: bias current is set to half and
++ *   is set to 1/4 with cable < 10m.
++ * - With DSP bias current (included both DSP amplitude and
++ *   DSP bias current): bias current is half the detected current
++ *   with cable < 10m.
++ */
++#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK                        BIT(2)
++#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT                  BIT(1)
++#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE                     BIT(0)
++
++#define QCA807X_MMD7_LED_100N_1                               0x8074
++#define QCA807X_MMD7_LED_100N_2                               0x8075
++#define QCA807X_MMD7_LED_1000N_1                      0x8076
++#define QCA807X_MMD7_LED_1000N_2                      0x8077
++
++#define QCA807X_MMD7_LED_CTRL(x)                      (0x8074 + ((x) * 2))
++#define QCA807X_MMD7_LED_FORCE_CTRL(x)                        (0x8075 + ((x) * 2))
++
++#define QCA807X_GPIO_FORCE_EN                         BIT(15)
++#define QCA807X_GPIO_FORCE_MODE_MASK                  GENMASK(14, 13)
++
++#define QCA807X_FUNCTION_CONTROL                      0x10
++#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK            GENMASK(6, 5)
++#define QCA807X_FC_MDI_CROSSOVER_AUTO                 3
++#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX          1
++#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI           0
++
++/* PQSGMII Analog PHY specific */
++#define PQSGMII_CTRL_REG                              0x0
++#define PQSGMII_ANALOG_SW_RESET                               BIT(6)
++#define PQSGMII_DRIVE_CONTROL_1                               0xb
++#define PQSGMII_TX_DRIVER_MASK                                GENMASK(7, 4)
++#define PQSGMII_TX_DRIVER_140MV                               0x0
++#define PQSGMII_TX_DRIVER_160MV                               0x1
++#define PQSGMII_TX_DRIVER_180MV                               0x2
++#define PQSGMII_TX_DRIVER_200MV                               0x3
++#define PQSGMII_TX_DRIVER_220MV                               0x4
++#define PQSGMII_TX_DRIVER_240MV                               0x5
++#define PQSGMII_TX_DRIVER_260MV                               0x6
++#define PQSGMII_TX_DRIVER_280MV                               0x7
++#define PQSGMII_TX_DRIVER_300MV                               0x8
++#define PQSGMII_TX_DRIVER_320MV                               0x9
++#define PQSGMII_TX_DRIVER_400MV                               0xa
++#define PQSGMII_TX_DRIVER_500MV                               0xb
++#define PQSGMII_TX_DRIVER_600MV                               0xc
++#define PQSGMII_MODE_CTRL                             0x6d
++#define PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK          BIT(0)
++#define PQSGMII_MMD3_SERDES_CONTROL                   0x805a
++
++#define PHY_ID_QCA8072                0x004dd0b2
++#define PHY_ID_QCA8075                0x004dd0b1
++
++#define QCA807X_COMBO_ADDR_OFFSET                     4
++#define QCA807X_PQSGMII_ADDR_OFFSET                   5
++#define SERDES_RESET_SLEEP                            100
++
++enum qca807x_global_phy {
++      QCA807X_COMBO_ADDR = 4,
++      QCA807X_PQSGMII_ADDR = 5,
++};
++
++struct qca807x_shared_priv {
++      unsigned int package_mode;
++      u32 tx_drive_strength;
++};
++
++struct qca807x_gpio_priv {
++      struct phy_device *phy;
++};
++
++struct qca807x_priv {
++      bool dac_full_amplitude;
++      bool dac_full_bias_current;
++      bool dac_disable_bias_current_tweak;
++};
++
++static int qca807x_cable_test_start(struct phy_device *phydev)
++{
++      /* we do all the (time consuming) work later */
++      return 0;
++}
++
++#ifdef CONFIG_GPIOLIB
++static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
++{
++      return GPIO_LINE_DIRECTION_OUT;
++}
++
++static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset)
++{
++      struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
++      u16 reg;
++      int val;
++
++      reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
++      val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
++
++      return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val);
++}
++
++static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
++{
++      struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
++      u16 reg;
++      int val;
++
++      reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
++
++      val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
++      val &= ~QCA807X_GPIO_FORCE_MODE_MASK;
++      val |= QCA807X_GPIO_FORCE_EN;
++      val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value);
++
++      phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val);
++}
++
++static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value)
++{
++      qca807x_gpio_set(gc, offset, value);
++
++      return 0;
++}
++
++static int qca807x_gpio(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      struct qca807x_gpio_priv *priv;
++      struct gpio_chip *gc;
++
++      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      priv->phy = phydev;
++
++      gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
++      if (!gc)
++              return -ENOMEM;
++
++      gc->label = dev_name(dev);
++      gc->base = -1;
++      gc->ngpio = 2;
++      gc->parent = dev;
++      gc->owner = THIS_MODULE;
++      gc->can_sleep = true;
++      gc->get_direction = qca807x_gpio_get_direction;
++      gc->direction_output = qca807x_gpio_dir_out;
++      gc->get = qca807x_gpio_get;
++      gc->set = qca807x_gpio_set;
++
++      return devm_gpiochip_add_data(dev, gc, priv);
++}
++#endif
++
++static int qca807x_read_fiber_status(struct phy_device *phydev)
++{
++      bool changed;
++      int ss, err;
++
++      err = genphy_c37_read_status(phydev, &changed);
++      if (err || !changed)
++              return err;
++
++      /* Read the QCA807x PHY-Specific Status register fiber page,
++       * which indicates the speed and duplex that the PHY is actually
++       * using, irrespective of whether we are in autoneg mode or not.
++       */
++      ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
++      if (ss < 0)
++              return ss;
++
++      phydev->speed = SPEED_UNKNOWN;
++      phydev->duplex = DUPLEX_UNKNOWN;
++      if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
++              switch (FIELD_GET(AT803X_SS_SPEED_MASK, ss)) {
++              case AT803X_SS_SPEED_100:
++                      phydev->speed = SPEED_100;
++                      break;
++              case AT803X_SS_SPEED_1000:
++                      phydev->speed = SPEED_1000;
++                      break;
++              }
++
++              if (ss & AT803X_SS_DUPLEX)
++                      phydev->duplex = DUPLEX_FULL;
++              else
++                      phydev->duplex = DUPLEX_HALF;
++      }
++
++      return 0;
++}
++
++static int qca807x_read_status(struct phy_device *phydev)
++{
++      if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
++              switch (phydev->port) {
++              case PORT_FIBRE:
++                      return qca807x_read_fiber_status(phydev);
++              case PORT_TP:
++                      return at803x_read_status(phydev);
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      return at803x_read_status(phydev);
++}
++
++static int qca807x_phy_package_probe_once(struct phy_device *phydev)
++{
++      struct phy_package_shared *shared = phydev->shared;
++      struct qca807x_shared_priv *priv = shared->priv;
++      unsigned int tx_drive_strength;
++      const char *package_mode_name;
++
++      /* Default to 600mw if not defined */
++      if (of_property_read_u32(shared->np, "qcom,tx-drive-strength-milliwatt",
++                               &tx_drive_strength))
++              tx_drive_strength = 600;
++
++      switch (tx_drive_strength) {
++      case 140:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_140MV;
++              break;
++      case 160:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_160MV;
++              break;
++      case 180:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_180MV;
++              break;
++      case 200:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_200MV;
++              break;
++      case 220:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_220MV;
++              break;
++      case 240:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_240MV;
++              break;
++      case 260:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_260MV;
++              break;
++      case 280:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_280MV;
++              break;
++      case 300:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_300MV;
++              break;
++      case 320:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_320MV;
++              break;
++      case 400:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_400MV;
++              break;
++      case 500:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_500MV;
++              break;
++      case 600:
++              priv->tx_drive_strength = PQSGMII_TX_DRIVER_600MV;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      priv->package_mode = PHY_INTERFACE_MODE_NA;
++      if (!of_property_read_string(shared->np, "qcom,package-mode",
++                                   &package_mode_name)) {
++              if (!strcasecmp(package_mode_name,
++                              phy_modes(PHY_INTERFACE_MODE_PSGMII)))
++                      priv->package_mode = PHY_INTERFACE_MODE_PSGMII;
++              else if (!strcasecmp(package_mode_name,
++                                   phy_modes(PHY_INTERFACE_MODE_QSGMII)))
++                      priv->package_mode = PHY_INTERFACE_MODE_QSGMII;
++              else
++                      return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int qca807x_phy_package_config_init_once(struct phy_device *phydev)
++{
++      struct phy_package_shared *shared = phydev->shared;
++      struct qca807x_shared_priv *priv = shared->priv;
++      int val, ret;
++
++      phy_lock_mdio_bus(phydev);
++
++      /* Set correct PHY package mode */
++      val = __phy_package_read(phydev, QCA807X_COMBO_ADDR,
++                               QCA807X_CHIP_CONFIGURATION);
++      val &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK;
++      /* package_mode can be QSGMII or PSGMII and we validate
++       * this in probe_once.
++       * With package_mode to NA, we default to PSGMII.
++       */
++      switch (priv->package_mode) {
++      case PHY_INTERFACE_MODE_QSGMII:
++              val |= QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII;
++              break;
++      case PHY_INTERFACE_MODE_PSGMII:
++      default:
++              val |= QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER;
++      }
++      ret = __phy_package_write(phydev, QCA807X_COMBO_ADDR,
++                                QCA807X_CHIP_CONFIGURATION, val);
++      if (ret)
++              goto exit;
++
++      /* After mode change Serdes reset is required */
++      val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
++                               PQSGMII_CTRL_REG);
++      val &= ~PQSGMII_ANALOG_SW_RESET;
++      ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
++                                PQSGMII_CTRL_REG, val);
++      if (ret)
++              goto exit;
++
++      msleep(SERDES_RESET_SLEEP);
++
++      val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
++                               PQSGMII_CTRL_REG);
++      val |= PQSGMII_ANALOG_SW_RESET;
++      ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
++                                PQSGMII_CTRL_REG, val);
++      if (ret)
++              goto exit;
++
++      /* Workaround to enable AZ transmitting ability */
++      val = __phy_package_read_mmd(phydev, QCA807X_PQSGMII_ADDR,
++                                   MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL);
++      val &= ~PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK;
++      ret = __phy_package_write_mmd(phydev, QCA807X_PQSGMII_ADDR,
++                                    MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL, val);
++      if (ret)
++              goto exit;
++
++      /* Set PQSGMII TX AMP strength */
++      val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
++                               PQSGMII_DRIVE_CONTROL_1);
++      val &= ~PQSGMII_TX_DRIVER_MASK;
++      val |= FIELD_PREP(PQSGMII_TX_DRIVER_MASK, priv->tx_drive_strength);
++      ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
++                                PQSGMII_DRIVE_CONTROL_1, val);
++      if (ret)
++              goto exit;
++
++      /* Prevent PSGMII going into hibernation via PSGMII self test */
++      val = __phy_package_read_mmd(phydev, QCA807X_COMBO_ADDR,
++                                   MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL);
++      val &= ~BIT(1);
++      ret = __phy_package_write_mmd(phydev, QCA807X_COMBO_ADDR,
++                                    MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL, val);
++
++exit:
++      phy_unlock_mdio_bus(phydev);
++
++      return ret;
++}
++
++static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++{
++      struct phy_device *phydev = upstream;
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
++      phy_interface_t iface;
++      int ret;
++      DECLARE_PHY_INTERFACE_MASK(interfaces);
++
++      sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
++      iface = sfp_select_interface(phydev->sfp_bus, support);
++
++      dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));
++
++      switch (iface) {
++      case PHY_INTERFACE_MODE_1000BASEX:
++      case PHY_INTERFACE_MODE_100BASEX:
++              /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */
++              ret = phy_modify(phydev,
++                               QCA807X_CHIP_CONFIGURATION,
++                               QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,
++                               QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);
++              /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
++              ret = phy_set_bits_mmd(phydev,
++                                     MDIO_MMD_AN,
++                                     QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,
++                                     QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN);
++              /* Select fiber page */
++              ret = phy_clear_bits(phydev,
++                                   QCA807X_CHIP_CONFIGURATION,
++                                   QCA807X_BT_BX_REG_SEL);
++
++              phydev->port = PORT_FIBRE;
++              break;
++      default:
++              dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n");
++              return -EINVAL;
++      }
++
++      return ret;
++}
++
++static void qca807x_sfp_remove(void *upstream)
++{
++      struct phy_device *phydev = upstream;
++
++      /* Select copper page */
++      phy_set_bits(phydev,
++                   QCA807X_CHIP_CONFIGURATION,
++                   QCA807X_BT_BX_REG_SEL);
++
++      phydev->port = PORT_TP;
++}
++
++static const struct sfp_upstream_ops qca807x_sfp_ops = {
++      .attach = phy_sfp_attach,
++      .detach = phy_sfp_detach,
++      .module_insert = qca807x_sfp_insert,
++      .module_remove = qca807x_sfp_remove,
++};
++
++static int qca807x_probe(struct phy_device *phydev)
++{
++      struct device_node *node = phydev->mdio.dev.of_node;
++      struct qca807x_shared_priv *shared_priv;
++      struct device *dev = &phydev->mdio.dev;
++      struct phy_package_shared *shared;
++      struct qca807x_priv *priv;
++      int ret;
++
++      ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
++      if (ret)
++              return ret;
++
++      if (phy_package_probe_once(phydev)) {
++              ret = qca807x_phy_package_probe_once(phydev);
++              if (ret)
++                      return ret;
++      }
++
++      shared = phydev->shared;
++      shared_priv = shared->priv;
++
++      /* Make sure PHY follow PHY package mode if enforced */
++      if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA &&
++          phydev->interface != shared_priv->package_mode)
++              return -EINVAL;
++
++      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      priv->dac_full_amplitude = of_property_read_bool(node, "qcom,dac-full-amplitude");
++      priv->dac_full_bias_current = of_property_read_bool(node, "qcom,dac-full-bias-current");
++      priv->dac_disable_bias_current_tweak = of_property_read_bool(node,
++                                                                   "qcom,dac-disable-bias-current-tweak");
++
++      if (IS_ENABLED(CONFIG_GPIOLIB)) {
++              /* Do not register a GPIO controller unless flagged for it */
++              if (of_property_read_bool(node, "gpio-controller")) {
++                      ret = qca807x_gpio(phydev);
++                      if (ret)
++                              return ret;
++              }
++      }
++
++      /* Attach SFP bus on combo port*/
++      if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
++              ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
++              if (ret)
++                      return ret;
++              linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
++              linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
++      }
++
++      phydev->priv = priv;
++
++      return 0;
++}
++
++static int qca807x_config_init(struct phy_device *phydev)
++{
++      struct qca807x_priv *priv = phydev->priv;
++      u16 control_dac;
++      int ret;
++
++      if (phy_package_init_once(phydev)) {
++              ret = qca807x_phy_package_config_init_once(phydev);
++              if (ret)
++                      return ret;
++      }
++
++      control_dac = phy_read_mmd(phydev, MDIO_MMD_AN,
++                                 QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH);
++      control_dac &= ~QCA807X_CONTROL_DAC_MASK;
++      if (!priv->dac_full_amplitude)
++              control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE;
++      if (!priv->dac_full_amplitude)
++              control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT;
++      if (!priv->dac_disable_bias_current_tweak)
++              control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK;
++      return phy_write_mmd(phydev, MDIO_MMD_AN,
++                           QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH,
++                           control_dac);
++}
++
++static struct phy_driver qca807x_drivers[] = {
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_QCA8072),
++              .name           = "Qualcomm QCA8072",
++              .flags          = PHY_POLL_CABLE_TEST,
++              /* PHY_GBIT_FEATURES */
++              .probe          = qca807x_probe,
++              .config_init    = qca807x_config_init,
++              .read_status    = qca807x_read_status,
++              .config_intr    = at803x_config_intr,
++              .handle_interrupt = at803x_handle_interrupt,
++              .soft_reset     = genphy_soft_reset,
++              .get_tunable    = at803x_get_tunable,
++              .set_tunable    = at803x_set_tunable,
++              .resume         = genphy_resume,
++              .suspend        = genphy_suspend,
++              .cable_test_start       = qca807x_cable_test_start,
++              .cable_test_get_status  = qca808x_cable_test_get_status,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),
++              .name           = "Qualcomm QCA8075",
++              .flags          = PHY_POLL_CABLE_TEST,
++              /* PHY_GBIT_FEATURES */
++              .probe          = qca807x_probe,
++              .config_init    = qca807x_config_init,
++              .read_status    = qca807x_read_status,
++              .config_intr    = at803x_config_intr,
++              .handle_interrupt = at803x_handle_interrupt,
++              .soft_reset     = genphy_soft_reset,
++              .get_tunable    = at803x_get_tunable,
++              .set_tunable    = at803x_set_tunable,
++              .resume         = genphy_resume,
++              .suspend        = genphy_suspend,
++              .cable_test_start       = qca807x_cable_test_start,
++              .cable_test_get_status  = qca808x_cable_test_get_status,
++      },
++};
++module_phy_driver(qca807x_drivers);
++
++static struct mdio_device_id __maybe_unused qca807x_tbl[] = {
++      { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) },
++      { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) },
++      { }
++};
++
++MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
++MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
++MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver");
++MODULE_DEVICE_TABLE(mdio, qca807x_tbl);
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.6/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch b/target/linux/generic/backport-6.6/716-v6.9-08-net-phy-qcom-move-common-qca808x-LED-define-to-share.patch
new file mode 100644 (file)
index 0000000..cf4d74e
--- /dev/null
@@ -0,0 +1,179 @@
+From ee9d9807bee0e6af8ca2a4db6f0d1dc0e5b41f44 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 6 Feb 2024 18:31:11 +0100
+Subject: [PATCH 08/10] net: phy: qcom: move common qca808x LED define to
+ shared header
+
+The LED implementation of qca808x and qca807x is the same but qca807x
+supports also Fiber port and have different hw control bits for Fiber
+port.
+
+In preparation for qca807x introduction, move all the common define to
+shared header.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 65 ----------------------------------
+ drivers/net/phy/qcom/qcom.h    | 65 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 65 insertions(+), 65 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -62,29 +62,6 @@
+ #define QCA808X_DBG_AN_TEST                   0xb
+ #define QCA808X_HIBERNATION_EN                        BIT(15)
+-#define QCA808X_MMD7_LED_GLOBAL                       0x8073
+-#define QCA808X_LED_BLINK_1                   GENMASK(11, 6)
+-#define QCA808X_LED_BLINK_2                   GENMASK(5, 0)
+-/* Values are the same for both BLINK_1 and BLINK_2 */
+-#define QCA808X_LED_BLINK_FREQ_MASK           GENMASK(5, 3)
+-#define QCA808X_LED_BLINK_FREQ_2HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
+-#define QCA808X_LED_BLINK_FREQ_4HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
+-#define QCA808X_LED_BLINK_FREQ_8HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
+-#define QCA808X_LED_BLINK_FREQ_16HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
+-#define QCA808X_LED_BLINK_FREQ_32HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
+-#define QCA808X_LED_BLINK_FREQ_64HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
+-#define QCA808X_LED_BLINK_FREQ_128HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
+-#define QCA808X_LED_BLINK_FREQ_256HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
+-#define QCA808X_LED_BLINK_DUTY_MASK           GENMASK(2, 0)
+-#define QCA808X_LED_BLINK_DUTY_50_50          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
+-#define QCA808X_LED_BLINK_DUTY_75_25          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
+-#define QCA808X_LED_BLINK_DUTY_25_75          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
+-#define QCA808X_LED_BLINK_DUTY_33_67          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
+-#define QCA808X_LED_BLINK_DUTY_67_33          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
+-#define QCA808X_LED_BLINK_DUTY_17_83          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
+-#define QCA808X_LED_BLINK_DUTY_83_17          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
+-#define QCA808X_LED_BLINK_DUTY_8_92           FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
+-
+ #define QCA808X_MMD7_LED2_CTRL                        0x8074
+ #define QCA808X_MMD7_LED2_FORCE_CTRL          0x8075
+ #define QCA808X_MMD7_LED1_CTRL                        0x8076
+@@ -92,51 +69,9 @@
+ #define QCA808X_MMD7_LED0_CTRL                        0x8078
+ #define QCA808X_MMD7_LED_CTRL(x)              (0x8078 - ((x) * 2))
+-/* LED hw control pattern is the same for every LED */
+-#define QCA808X_LED_PATTERN_MASK              GENMASK(15, 0)
+-#define QCA808X_LED_SPEED2500_ON              BIT(15)
+-#define QCA808X_LED_SPEED2500_BLINK           BIT(14)
+-/* Follow blink trigger even if duplex or speed condition doesn't match */
+-#define QCA808X_LED_BLINK_CHECK_BYPASS                BIT(13)
+-#define QCA808X_LED_FULL_DUPLEX_ON            BIT(12)
+-#define QCA808X_LED_HALF_DUPLEX_ON            BIT(11)
+-#define QCA808X_LED_TX_BLINK                  BIT(10)
+-#define QCA808X_LED_RX_BLINK                  BIT(9)
+-#define QCA808X_LED_TX_ON_10MS                        BIT(8)
+-#define QCA808X_LED_RX_ON_10MS                        BIT(7)
+-#define QCA808X_LED_SPEED1000_ON              BIT(6)
+-#define QCA808X_LED_SPEED100_ON                       BIT(5)
+-#define QCA808X_LED_SPEED10_ON                        BIT(4)
+-#define QCA808X_LED_COLLISION_BLINK           BIT(3)
+-#define QCA808X_LED_SPEED1000_BLINK           BIT(2)
+-#define QCA808X_LED_SPEED100_BLINK            BIT(1)
+-#define QCA808X_LED_SPEED10_BLINK             BIT(0)
+-
+ #define QCA808X_MMD7_LED0_FORCE_CTRL          0x8079
+ #define QCA808X_MMD7_LED_FORCE_CTRL(x)                (0x8079 - ((x) * 2))
+-/* LED force ctrl is the same for every LED
+- * No documentation exist for this, not even internal one
+- * with NDA as QCOM gives only info about configuring
+- * hw control pattern rules and doesn't indicate any way
+- * to force the LED to specific mode.
+- * These define comes from reverse and testing and maybe
+- * lack of some info or some info are not entirely correct.
+- * For the basic LED control and hw control these finding
+- * are enough to support LED control in all the required APIs.
+- *
+- * On doing some comparison with implementation with qca807x,
+- * it was found that it's 1:1 equal to it and confirms all the
+- * reverse done. It was also found further specification with the
+- * force mode and the blink modes.
+- */
+-#define QCA808X_LED_FORCE_EN                  BIT(15)
+-#define QCA808X_LED_FORCE_MODE_MASK           GENMASK(14, 13)
+-#define QCA808X_LED_FORCE_BLINK_1             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
+-#define QCA808X_LED_FORCE_BLINK_2             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
+-#define QCA808X_LED_FORCE_ON                  FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
+-#define QCA808X_LED_FORCE_OFF                 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
+-
+ #define QCA808X_MMD7_LED_POLARITY_CTRL                0x901a
+ /* QSDK sets by default 0x46 to this reg that sets BIT 6 for
+  * LED to active high. It's not clear what BIT 3 and BIT 4 does.
+--- a/drivers/net/phy/qcom/qcom.h
++++ b/drivers/net/phy/qcom/qcom.h
+@@ -103,6 +103,71 @@
+ /* Added for reference of existence but should be handled by wait_for_completion already */
+ #define QCA808X_CDT_STATUS_STAT_BUSY          (BIT(1) | BIT(3))
++#define QCA808X_MMD7_LED_GLOBAL                       0x8073
++#define QCA808X_LED_BLINK_1                   GENMASK(11, 6)
++#define QCA808X_LED_BLINK_2                   GENMASK(5, 0)
++/* Values are the same for both BLINK_1 and BLINK_2 */
++#define QCA808X_LED_BLINK_FREQ_MASK           GENMASK(5, 3)
++#define QCA808X_LED_BLINK_FREQ_2HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
++#define QCA808X_LED_BLINK_FREQ_4HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
++#define QCA808X_LED_BLINK_FREQ_8HZ            FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
++#define QCA808X_LED_BLINK_FREQ_16HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
++#define QCA808X_LED_BLINK_FREQ_32HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
++#define QCA808X_LED_BLINK_FREQ_64HZ           FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
++#define QCA808X_LED_BLINK_FREQ_128HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
++#define QCA808X_LED_BLINK_FREQ_256HZ          FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
++#define QCA808X_LED_BLINK_DUTY_MASK           GENMASK(2, 0)
++#define QCA808X_LED_BLINK_DUTY_50_50          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
++#define QCA808X_LED_BLINK_DUTY_75_25          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
++#define QCA808X_LED_BLINK_DUTY_25_75          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
++#define QCA808X_LED_BLINK_DUTY_33_67          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
++#define QCA808X_LED_BLINK_DUTY_67_33          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
++#define QCA808X_LED_BLINK_DUTY_17_83          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
++#define QCA808X_LED_BLINK_DUTY_83_17          FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
++#define QCA808X_LED_BLINK_DUTY_8_92           FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
++
++/* LED hw control pattern is the same for every LED */
++#define QCA808X_LED_PATTERN_MASK              GENMASK(15, 0)
++#define QCA808X_LED_SPEED2500_ON              BIT(15)
++#define QCA808X_LED_SPEED2500_BLINK           BIT(14)
++/* Follow blink trigger even if duplex or speed condition doesn't match */
++#define QCA808X_LED_BLINK_CHECK_BYPASS                BIT(13)
++#define QCA808X_LED_FULL_DUPLEX_ON            BIT(12)
++#define QCA808X_LED_HALF_DUPLEX_ON            BIT(11)
++#define QCA808X_LED_TX_BLINK                  BIT(10)
++#define QCA808X_LED_RX_BLINK                  BIT(9)
++#define QCA808X_LED_TX_ON_10MS                        BIT(8)
++#define QCA808X_LED_RX_ON_10MS                        BIT(7)
++#define QCA808X_LED_SPEED1000_ON              BIT(6)
++#define QCA808X_LED_SPEED100_ON                       BIT(5)
++#define QCA808X_LED_SPEED10_ON                        BIT(4)
++#define QCA808X_LED_COLLISION_BLINK           BIT(3)
++#define QCA808X_LED_SPEED1000_BLINK           BIT(2)
++#define QCA808X_LED_SPEED100_BLINK            BIT(1)
++#define QCA808X_LED_SPEED10_BLINK             BIT(0)
++
++/* LED force ctrl is the same for every LED
++ * No documentation exist for this, not even internal one
++ * with NDA as QCOM gives only info about configuring
++ * hw control pattern rules and doesn't indicate any way
++ * to force the LED to specific mode.
++ * These define comes from reverse and testing and maybe
++ * lack of some info or some info are not entirely correct.
++ * For the basic LED control and hw control these finding
++ * are enough to support LED control in all the required APIs.
++ *
++ * On doing some comparison with implementation with qca807x,
++ * it was found that it's 1:1 equal to it and confirms all the
++ * reverse done. It was also found further specification with the
++ * force mode and the blink modes.
++ */
++#define QCA808X_LED_FORCE_EN                  BIT(15)
++#define QCA808X_LED_FORCE_MODE_MASK           GENMASK(14, 13)
++#define QCA808X_LED_FORCE_BLINK_1             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
++#define QCA808X_LED_FORCE_BLINK_2             FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
++#define QCA808X_LED_FORCE_ON                  FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
++#define QCA808X_LED_FORCE_OFF                 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
++
+ #define AT803X_LOC_MAC_ADDR_0_15_OFFSET               0x804C
+ #define AT803X_LOC_MAC_ADDR_16_31_OFFSET      0x804B
+ #define AT803X_LOC_MAC_ADDR_32_47_OFFSET      0x804A
diff --git a/target/linux/generic/backport-6.6/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch b/target/linux/generic/backport-6.6/716-v6.9-09-net-phy-qcom-generalize-some-qca808x-LED-functions.patch
new file mode 100644 (file)
index 0000000..da73c1d
--- /dev/null
@@ -0,0 +1,172 @@
+From 47b930d0dd437af927145dba50a2e2ea1ba97c67 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 6 Feb 2024 18:31:12 +0100
+Subject: [PATCH 09/10] net: phy: qcom: generalize some qca808x LED functions
+
+Generalize some qca808x LED functions in preparation for qca807x LED
+support.
+
+The LED implementation of qca808x and qca807x is the same but qca807x
+supports also Fiber port and have different hw control bits for Fiber
+port. To limit code duplication introduce micro functions that takes reg
+instead of LED index to tweak all the supported LED modes.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c      | 38 +++-----------------
+ drivers/net/phy/qcom/qcom-phy-lib.c | 54 +++++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qcom.h         |  7 ++++
+ 3 files changed, 65 insertions(+), 34 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -437,9 +437,7 @@ static int qca808x_led_hw_control_enable
+               return -EINVAL;
+       reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
+-                                QCA808X_LED_FORCE_EN);
++      return qca808x_led_reg_hw_control_enable(phydev, reg);
+ }
+ static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
+@@ -480,16 +478,12 @@ static int qca808x_led_hw_control_set(st
+ static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
+ {
+       u16 reg;
+-      int val;
+       if (index > 2)
+               return false;
+       reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
+-
+-      return !(val & QCA808X_LED_FORCE_EN);
++      return qca808x_led_reg_hw_control_status(phydev, reg);
+ }
+ static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
+@@ -557,44 +551,20 @@ static int qca808x_led_brightness_set(st
+       }
+       reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-                            QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
+-                            QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
+-                                                           QCA808X_LED_FORCE_OFF));
++      return qca808x_led_reg_brightness_set(phydev, reg, value);
+ }
+ static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
+                                unsigned long *delay_on,
+                                unsigned long *delay_off)
+ {
+-      int ret;
+       u16 reg;
+       if (index > 2)
+               return -EINVAL;
+       reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
+-
+-      /* Set blink to 50% off, 50% on at 4Hz by default */
+-      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
+-                           QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
+-                           QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
+-      if (ret)
+-              return ret;
+-
+-      /* We use BLINK_1 for normal blinking */
+-      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
+-                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
+-                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
+-      if (ret)
+-              return ret;
+-
+-      /* We set blink to 4Hz, aka 250ms */
+-      *delay_on = 250 / 2;
+-      *delay_off = 250 / 2;
+-
+-      return 0;
++      return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
+ }
+ static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
+--- a/drivers/net/phy/qcom/qcom-phy-lib.c
++++ b/drivers/net/phy/qcom/qcom-phy-lib.c
+@@ -620,3 +620,57 @@ int qca808x_cable_test_get_status(struct
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
++
++int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg)
++{
++      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++                                QCA808X_LED_FORCE_EN);
++}
++EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable);
++
++bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg)
++{
++      int val;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++      return !(val & QCA808X_LED_FORCE_EN);
++}
++EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status);
++
++int qca808x_led_reg_brightness_set(struct phy_device *phydev,
++                                 u16 reg, enum led_brightness value)
++{
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                            QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++                            QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
++                                                            QCA808X_LED_FORCE_OFF));
++}
++EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set);
++
++int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
++                            unsigned long *delay_on,
++                            unsigned long *delay_off)
++{
++      int ret;
++
++      /* Set blink to 50% off, 50% on at 4Hz by default */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
++                           QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
++                           QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
++      if (ret)
++              return ret;
++
++      /* We use BLINK_1 for normal blinking */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++                           QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
++      if (ret)
++              return ret;
++
++      /* We set blink to 4Hz, aka 250ms */
++      *delay_on = 250 / 2;
++      *delay_off = 250 / 2;
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);
+--- a/drivers/net/phy/qcom/qcom.h
++++ b/drivers/net/phy/qcom/qcom.h
+@@ -234,3 +234,10 @@ int at803x_cdt_start(struct phy_device *
+ int at803x_cdt_wait_for_completion(struct phy_device *phydev,
+                                  u32 cdt_en);
+ int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
++int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg);
++bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg);
++int qca808x_led_reg_brightness_set(struct phy_device *phydev,
++                                 u16 reg, enum led_brightness value);
++int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
++                            unsigned long *delay_on,
++                            unsigned long *delay_off);
diff --git a/target/linux/generic/backport-6.6/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch b/target/linux/generic/backport-6.6/716-v6.9-10-net-phy-qca807x-add-support-for-configurable-LED.patch
new file mode 100644 (file)
index 0000000..3bd36f6
--- /dev/null
@@ -0,0 +1,326 @@
+From f508a226b517a6a8afd78a317de46bc83e3e3d51 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 6 Feb 2024 18:31:13 +0100
+Subject: [PATCH 10/10] net: phy: qca807x: add support for configurable LED
+
+QCA8072/5 have up to 2 LEDs attached for PHY.
+
+LEDs can be configured to be ON/hw blink or be set to HW control.
+
+Hw blink mode is set to blink at 4Hz or 250ms.
+
+PHY can support both copper (TP) or fiber (FIBRE) kind and supports
+different HW control modes based on the port type.
+
+HW control modes supported for netdev trigger for copper ports are:
+- LINK_10
+- LINK_100
+- LINK_1000
+- TX
+- RX
+- FULL_DUPLEX
+- HALF_DUPLEX
+
+HW control modes supported for netdev trigger for fiber ports are:
+- LINK_100
+- LINK_1000
+- TX
+- RX
+- FULL_DUPLEX
+- HALF_DUPLEX
+
+LED support conflicts with GPIO controller feature and must be disabled
+if gpio-controller is used for the PHY.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca807x.c | 256 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 254 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca807x.c
++++ b/drivers/net/phy/qcom/qca807x.c
+@@ -57,8 +57,18 @@
+ #define QCA807X_MMD7_LED_CTRL(x)                      (0x8074 + ((x) * 2))
+ #define QCA807X_MMD7_LED_FORCE_CTRL(x)                        (0x8075 + ((x) * 2))
+-#define QCA807X_GPIO_FORCE_EN                         BIT(15)
+-#define QCA807X_GPIO_FORCE_MODE_MASK                  GENMASK(14, 13)
++/* LED hw control pattern for fiber port */
++#define QCA807X_LED_FIBER_PATTERN_MASK                        GENMASK(11, 1)
++#define QCA807X_LED_FIBER_TXACT_BLK_EN                        BIT(10)
++#define QCA807X_LED_FIBER_RXACT_BLK_EN                        BIT(9)
++#define QCA807X_LED_FIBER_FDX_ON_EN                   BIT(6)
++#define QCA807X_LED_FIBER_HDX_ON_EN                   BIT(5)
++#define QCA807X_LED_FIBER_1000BX_ON_EN                        BIT(2)
++#define QCA807X_LED_FIBER_100FX_ON_EN                 BIT(1)
++
++/* Some device repurpose the LED as GPIO out */
++#define QCA807X_GPIO_FORCE_EN                         QCA808X_LED_FORCE_EN
++#define QCA807X_GPIO_FORCE_MODE_MASK                  QCA808X_LED_FORCE_MODE_MASK
+ #define QCA807X_FUNCTION_CONTROL                      0x10
+ #define QCA807X_FC_MDI_CROSSOVER_MODE_MASK            GENMASK(6, 5)
+@@ -121,6 +131,233 @@ static int qca807x_cable_test_start(stru
+       return 0;
+ }
++static int qca807x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
++                                  u16 *offload_trigger)
++{
++      /* Parsing specific to netdev trigger */
++      switch (phydev->port) {
++      case PORT_TP:
++              if (test_bit(TRIGGER_NETDEV_TX, &rules))
++                      *offload_trigger |= QCA808X_LED_TX_BLINK;
++              if (test_bit(TRIGGER_NETDEV_RX, &rules))
++                      *offload_trigger |= QCA808X_LED_RX_BLINK;
++              if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
++                      *offload_trigger |= QCA808X_LED_SPEED10_ON;
++              if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++                      *offload_trigger |= QCA808X_LED_SPEED100_ON;
++              if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++                      *offload_trigger |= QCA808X_LED_SPEED1000_ON;
++              if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
++                      *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
++              if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
++                      *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
++              break;
++      case PORT_FIBRE:
++              if (test_bit(TRIGGER_NETDEV_TX, &rules))
++                      *offload_trigger |= QCA807X_LED_FIBER_TXACT_BLK_EN;
++              if (test_bit(TRIGGER_NETDEV_RX, &rules))
++                      *offload_trigger |= QCA807X_LED_FIBER_RXACT_BLK_EN;
++              if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++                      *offload_trigger |= QCA807X_LED_FIBER_100FX_ON_EN;
++              if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++                      *offload_trigger |= QCA807X_LED_FIBER_1000BX_ON_EN;
++              if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
++                      *offload_trigger |= QCA807X_LED_FIBER_HDX_ON_EN;
++              if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
++                      *offload_trigger |= QCA807X_LED_FIBER_FDX_ON_EN;
++              break;
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      if (rules && !*offload_trigger)
++              return -EOPNOTSUPP;
++
++      return 0;
++}
++
++static int qca807x_led_hw_control_enable(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++
++      if (index > 1)
++              return -EINVAL;
++
++      reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++      return qca808x_led_reg_hw_control_enable(phydev, reg);
++}
++
++static int qca807x_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                     unsigned long rules)
++{
++      u16 offload_trigger = 0;
++
++      if (index > 1)
++              return -EINVAL;
++
++      return qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
++}
++
++static int qca807x_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                    unsigned long rules)
++{
++      u16 reg, mask, offload_trigger = 0;
++      int ret;
++
++      if (index > 1)
++              return -EINVAL;
++
++      ret = qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
++      if (ret)
++              return ret;
++
++      ret = qca807x_led_hw_control_enable(phydev, index);
++      if (ret)
++              return ret;
++
++      switch (phydev->port) {
++      case PORT_TP:
++              reg = QCA807X_MMD7_LED_CTRL(index);
++              mask = QCA808X_LED_PATTERN_MASK;
++              break;
++      case PORT_FIBRE:
++              /* HW control pattern bits are in LED FORCE reg */
++              reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++              mask = QCA807X_LED_FIBER_PATTERN_MASK;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, mask,
++                            offload_trigger);
++}
++
++static bool qca807x_led_hw_control_status(struct phy_device *phydev, u8 index)
++{
++      u16 reg;
++
++      if (index > 1)
++              return false;
++
++      reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++      return qca808x_led_reg_hw_control_status(phydev, reg);
++}
++
++static int qca807x_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                    unsigned long *rules)
++{
++      u16 reg;
++      int val;
++
++      if (index > 1)
++              return -EINVAL;
++
++      /* Check if we have hw control enabled */
++      if (qca807x_led_hw_control_status(phydev, index))
++              return -EINVAL;
++
++      /* Parsing specific to netdev trigger */
++      switch (phydev->port) {
++      case PORT_TP:
++              reg = QCA807X_MMD7_LED_CTRL(index);
++              val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++              if (val & QCA808X_LED_TX_BLINK)
++                      set_bit(TRIGGER_NETDEV_TX, rules);
++              if (val & QCA808X_LED_RX_BLINK)
++                      set_bit(TRIGGER_NETDEV_RX, rules);
++              if (val & QCA808X_LED_SPEED10_ON)
++                      set_bit(TRIGGER_NETDEV_LINK_10, rules);
++              if (val & QCA808X_LED_SPEED100_ON)
++                      set_bit(TRIGGER_NETDEV_LINK_100, rules);
++              if (val & QCA808X_LED_SPEED1000_ON)
++                      set_bit(TRIGGER_NETDEV_LINK_1000, rules);
++              if (val & QCA808X_LED_HALF_DUPLEX_ON)
++                      set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
++              if (val & QCA808X_LED_FULL_DUPLEX_ON)
++                      set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
++              break;
++      case PORT_FIBRE:
++              /* HW control pattern bits are in LED FORCE reg */
++              reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++              val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++              if (val & QCA807X_LED_FIBER_TXACT_BLK_EN)
++                      set_bit(TRIGGER_NETDEV_TX, rules);
++              if (val & QCA807X_LED_FIBER_RXACT_BLK_EN)
++                      set_bit(TRIGGER_NETDEV_RX, rules);
++              if (val & QCA807X_LED_FIBER_100FX_ON_EN)
++                      set_bit(TRIGGER_NETDEV_LINK_100, rules);
++              if (val & QCA807X_LED_FIBER_1000BX_ON_EN)
++                      set_bit(TRIGGER_NETDEV_LINK_1000, rules);
++              if (val & QCA807X_LED_FIBER_HDX_ON_EN)
++                      set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
++              if (val & QCA807X_LED_FIBER_FDX_ON_EN)
++                      set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int qca807x_led_hw_control_reset(struct phy_device *phydev, u8 index)
++{
++      u16 reg, mask;
++
++      if (index > 1)
++              return -EINVAL;
++
++      switch (phydev->port) {
++      case PORT_TP:
++              reg = QCA807X_MMD7_LED_CTRL(index);
++              mask = QCA808X_LED_PATTERN_MASK;
++              break;
++      case PORT_FIBRE:
++              /* HW control pattern bits are in LED FORCE reg */
++              reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++              mask = QCA807X_LED_FIBER_PATTERN_MASK;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, mask);
++}
++
++static int qca807x_led_brightness_set(struct phy_device *phydev,
++                                    u8 index, enum led_brightness value)
++{
++      u16 reg;
++      int ret;
++
++      if (index > 1)
++              return -EINVAL;
++
++      /* If we are setting off the LED reset any hw control rule */
++      if (!value) {
++              ret = qca807x_led_hw_control_reset(phydev, index);
++              if (ret)
++                      return ret;
++      }
++
++      reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++      return qca808x_led_reg_brightness_set(phydev, reg, value);
++}
++
++static int qca807x_led_blink_set(struct phy_device *phydev, u8 index,
++                               unsigned long *delay_on,
++                               unsigned long *delay_off)
++{
++      u16 reg;
++
++      if (index > 1)
++              return -EINVAL;
++
++      reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
++      return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
++}
++
+ #ifdef CONFIG_GPIOLIB
+ static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+ {
+@@ -496,6 +733,16 @@ static int qca807x_probe(struct phy_devi
+                                                                    "qcom,dac-disable-bias-current-tweak");
+       if (IS_ENABLED(CONFIG_GPIOLIB)) {
++              /* Make sure we don't have mixed leds node and gpio-controller
++               * to prevent registering leds and having gpio-controller usage
++               * conflicting with them.
++               */
++              if (of_find_property(node, "leds", NULL) &&
++                  of_find_property(node, "gpio-controller", NULL)) {
++                      phydev_err(phydev, "Invalid property detected. LEDs and gpio-controller are mutually exclusive.");
++                      return -EINVAL;
++              }
++
+               /* Do not register a GPIO controller unless flagged for it */
+               if (of_property_read_bool(node, "gpio-controller")) {
+                       ret = qca807x_gpio(phydev);
+@@ -580,6 +827,11 @@ static struct phy_driver qca807x_drivers
+               .suspend        = genphy_suspend,
+               .cable_test_start       = qca807x_cable_test_start,
+               .cable_test_get_status  = qca808x_cable_test_get_status,
++              .led_brightness_set = qca807x_led_brightness_set,
++              .led_blink_set = qca807x_led_blink_set,
++              .led_hw_is_supported = qca807x_led_hw_is_supported,
++              .led_hw_control_set = qca807x_led_hw_control_set,
++              .led_hw_control_get = qca807x_led_hw_control_get,
+       },
+ };
+ module_phy_driver(qca807x_drivers);
diff --git a/target/linux/generic/backport-6.6/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch b/target/linux/generic/backport-6.6/717-v6.9-net-phy-qca807x-move-interface-mode-check-to-.config.patch
new file mode 100644 (file)
index 0000000..53652c3
--- /dev/null
@@ -0,0 +1,51 @@
+From 3be0d950b62852a693182cb678948f481de02825 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Mon, 12 Feb 2024 12:49:34 +0100
+Subject: [PATCH] net: phy: qca807x: move interface mode check to
+ .config_init_once
+
+Currently, we are checking whether the PHY package mode matches the
+individual PHY interface modes at PHY package probe time, but at that time
+we only know the PHY package mode and not the individual PHY interface
+modes as of_get_phy_mode() that populates it will only get called once the
+netdev to which PHY-s are attached to is being probed and thus this check
+will always fail and return -EINVAL.
+
+So, lets move this check to .config_init_once as at that point individual
+PHY interface modes should be populated.
+
+Fixes: d1cb613efbd3 ("net: phy: qcom: add support for QCA807x PHY Family")
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240212115043.1725918-1-robimarko@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/qcom/qca807x.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca807x.c
++++ b/drivers/net/phy/qcom/qca807x.c
+@@ -562,6 +562,11 @@ static int qca807x_phy_package_config_in
+       struct qca807x_shared_priv *priv = shared->priv;
+       int val, ret;
++      /* Make sure PHY follow PHY package mode if enforced */
++      if (priv->package_mode != PHY_INTERFACE_MODE_NA &&
++          phydev->interface != priv->package_mode)
++              return -EINVAL;
++
+       phy_lock_mdio_bus(phydev);
+       /* Set correct PHY package mode */
+@@ -718,11 +723,6 @@ static int qca807x_probe(struct phy_devi
+       shared = phydev->shared;
+       shared_priv = shared->priv;
+-      /* Make sure PHY follow PHY package mode if enforced */
+-      if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA &&
+-          phydev->interface != shared_priv->package_mode)
+-              return -EINVAL;
+-
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
diff --git a/target/linux/generic/backport-6.6/718-v6.9-net-phy-qcom-at803x-fix-kernel-panic-with-at8031_pro.patch b/target/linux/generic/backport-6.6/718-v6.9-net-phy-qcom-at803x-fix-kernel-panic-with-at8031_pro.patch
new file mode 100644 (file)
index 0000000..9b9ce2a
--- /dev/null
@@ -0,0 +1,45 @@
+From 6a4aee277740d04ac0fd54cfa17cc28261932ddc Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 25 Mar 2024 20:06:19 +0100
+Subject: [PATCH] net: phy: qcom: at803x: fix kernel panic with at8031_probe
+
+On reworking and splitting the at803x driver, in splitting function of
+at803x PHYs it was added a NULL dereference bug where priv is referenced
+before it's actually allocated and then is tried to write to for the
+is_1000basex and is_fiber variables in the case of at8031, writing on
+the wrong address.
+
+Fix this by correctly setting priv local variable only after
+at803x_probe is called and actually allocates priv in the phydev struct.
+
+Reported-by: William Wortel <wwortel@dorpstraat.com>
+Cc: <stable@vger.kernel.org>
+Fixes: 25d2ba94005f ("net: phy: at803x: move specific at8031 probe mode check to dedicated probe")
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240325190621.2665-1-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/qcom/at803x.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/qcom/at803x.c
++++ b/drivers/net/phy/qcom/at803x.c
+@@ -797,7 +797,7 @@ static int at8031_parse_dt(struct phy_de
+ static int at8031_probe(struct phy_device *phydev)
+ {
+-      struct at803x_priv *priv = phydev->priv;
++      struct at803x_priv *priv;
+       int mode_cfg;
+       int ccr;
+       int ret;
+@@ -806,6 +806,8 @@ static int at8031_probe(struct phy_devic
+       if (ret)
+               return ret;
++      priv = phydev->priv;
++
+       /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
+        * options.
+        */
diff --git a/target/linux/generic/backport-6.6/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch b/target/linux/generic/backport-6.6/720-v6.9-net-mdio-ipq4019-add-support-for-clock-frequency-pro.patch
new file mode 100644 (file)
index 0000000..6749d7b
--- /dev/null
@@ -0,0 +1,205 @@
+From bdce82e960d1205d118662f575cec39379984e34 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 31 Jan 2024 03:26:04 +0100
+Subject: [PATCH] net: mdio: ipq4019: add support for clock-frequency property
+
+The IPQ4019 MDIO internally divide the clock feed by AHB based on the
+MDIO_MODE reg. On reset or power up, the default value for the
+divider is 0xff that reflect the divider set to /256.
+
+This makes the MDC run at a very low rate, that is, considering AHB is
+always fixed to 100Mhz, a value of 390KHz.
+
+This hasn't have been a problem as MDIO wasn't used for time sensitive
+operation, it is now that on IPQ807x is usually mounted with PHY that
+requires MDIO to load their firmware (example Aquantia PHY).
+
+To handle this problem and permit to set the correct designed MDC
+frequency for the SoC add support for the standard "clock-frequency"
+property for the MDIO node.
+
+The divider supports value from /1 to /256 and the common value are to
+set it to /16 to reflect 6.25Mhz or to /8 on newer platform to reflect
+12.5Mhz.
+
+To scan if the requested rate is supported by the divider, loop with
+each supported divider and stop when the requested rate match the final
+rate with the current divider. An error is returned if the rate doesn't
+match any value.
+
+On MDIO reset, the divider is restored to the requested value to prevent
+any kind of downclocking caused by the divider reverting to a default
+value.
+
+To follow 802.3 spec of 2.5MHz of default value, if divider is set at
+/256 and "clock-frequency" is not set in DT, assume nobody set the
+divider and try to find the closest MDC rate to 2.5MHz. (in the case of
+AHB set to 100MHz, it's 1.5625MHz)
+
+While at is also document other bits of the MDIO_MODE reg to have a
+clear idea of what is actually applied there.
+
+Documentation of some BITs is skipped as they are marked as reserved and
+their usage is not clear (RES 11:9 GENPHY 16:13 RES1 19:17)
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/mdio/mdio-ipq4019.c | 109 ++++++++++++++++++++++++++++++--
+ 1 file changed, 103 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/mdio/mdio-ipq4019.c
++++ b/drivers/net/mdio/mdio-ipq4019.c
+@@ -14,6 +14,20 @@
+ #include <linux/clk.h>
+ #define MDIO_MODE_REG                         0x40
++#define   MDIO_MODE_MDC_MODE                  BIT(12)
++/* 0 = Clause 22, 1 = Clause 45 */
++#define   MDIO_MODE_C45                               BIT(8)
++#define   MDIO_MODE_DIV_MASK                  GENMASK(7, 0)
++#define     MDIO_MODE_DIV(x)                  FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1)
++#define     MDIO_MODE_DIV_1                   0x0
++#define     MDIO_MODE_DIV_2                   0x1
++#define     MDIO_MODE_DIV_4                   0x3
++#define     MDIO_MODE_DIV_8                   0x7
++#define     MDIO_MODE_DIV_16                  0xf
++#define     MDIO_MODE_DIV_32                  0x1f
++#define     MDIO_MODE_DIV_64                  0x3f
++#define     MDIO_MODE_DIV_128                 0x7f
++#define     MDIO_MODE_DIV_256                 0xff
+ #define MDIO_ADDR_REG                         0x44
+ #define MDIO_DATA_WRITE_REG                   0x48
+ #define MDIO_DATA_READ_REG                    0x4c
+@@ -26,9 +40,6 @@
+ #define MDIO_CMD_ACCESS_CODE_C45_WRITE        1
+ #define MDIO_CMD_ACCESS_CODE_C45_READ 2
+-/* 0 = Clause 22, 1 = Clause 45 */
+-#define MDIO_MODE_C45                         BIT(8)
+-
+ #define IPQ4019_MDIO_TIMEOUT  10000
+ #define IPQ4019_MDIO_SLEEP            10
+@@ -41,6 +52,7 @@ struct ipq4019_mdio_data {
+       void __iomem    *membase;
+       void __iomem *eth_ldo_rdy;
+       struct clk *mdio_clk;
++      unsigned int mdc_rate;
+ };
+ static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
+@@ -203,6 +215,38 @@ static int ipq4019_mdio_write_c22(struct
+       return 0;
+ }
++static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv)
++{
++      unsigned long ahb_rate;
++      int div;
++      u32 val;
++
++      /* If we don't have a clock for AHB use the fixed value */
++      ahb_rate = IPQ_MDIO_CLK_RATE;
++      if (priv->mdio_clk)
++              ahb_rate = clk_get_rate(priv->mdio_clk);
++
++      /* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1)
++       * While supported, internal documentation doesn't
++       * assure correct functionality of the MDIO bus
++       * with divider of 1, 2 or 4.
++       */
++      for (div = 8; div <= 256; div *= 2) {
++              /* The requested rate is supported by the div */
++              if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) {
++                      val = readl(priv->membase + MDIO_MODE_REG);
++                      val &= ~MDIO_MODE_DIV_MASK;
++                      val |= MDIO_MODE_DIV(div);
++                      writel(val, priv->membase + MDIO_MODE_REG);
++
++                      return 0;
++              }
++      }
++
++      /* The requested rate is not supported */
++      return -EINVAL;
++}
++
+ static int ipq_mdio_reset(struct mii_bus *bus)
+ {
+       struct ipq4019_mdio_data *priv = bus->priv;
+@@ -225,10 +269,58 @@ static int ipq_mdio_reset(struct mii_bus
+               return ret;
+       ret = clk_prepare_enable(priv->mdio_clk);
+-      if (ret == 0)
+-              mdelay(10);
++      if (ret)
++              return ret;
++
++      mdelay(10);
+-      return ret;
++      /* Restore MDC rate */
++      return ipq4019_mdio_set_div(priv);
++}
++
++static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev,
++                                       struct ipq4019_mdio_data *priv)
++{
++      unsigned long ahb_rate;
++      int div;
++      u32 val;
++
++      /* MDC rate defined in DT, we don't have to decide a default value */
++      if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",
++                                &priv->mdc_rate))
++              return;
++
++      /* If we don't have a clock for AHB use the fixed value */
++      ahb_rate = IPQ_MDIO_CLK_RATE;
++      if (priv->mdio_clk)
++              ahb_rate = clk_get_rate(priv->mdio_clk);
++
++      /* Check what is the current div set */
++      val = readl(priv->membase + MDIO_MODE_REG);
++      div = FIELD_GET(MDIO_MODE_DIV_MASK, val);
++
++      /* div is not set to the default value of /256
++       * Probably someone changed that (bootloader, other drivers)
++       * Keep this and don't overwrite it.
++       */
++      if (div != MDIO_MODE_DIV_256) {
++              priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1);
++              return;
++      }
++
++      /* If div is /256 assume nobody have set this value and
++       * try to find one MDC rate that is close the 802.3 spec of
++       * 2.5MHz
++       */
++      for (div = 256; div >= 8; div /= 2) {
++              /* Stop as soon as we found a divider that
++               * reached the closest value to 2.5MHz
++               */
++              if (DIV_ROUND_UP(ahb_rate, div) > 2500000)
++                      break;
++
++              priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div);
++      }
+ }
+ static int ipq4019_mdio_probe(struct platform_device *pdev)
+@@ -252,6 +344,11 @@ static int ipq4019_mdio_probe(struct pla
+       if (IS_ERR(priv->mdio_clk))
+               return PTR_ERR(priv->mdio_clk);
++      ipq4019_mdio_select_mdc_rate(pdev, priv);
++      ret = ipq4019_mdio_set_div(priv);
++      if (ret)
++              return ret;
++
+       /* The platform resource is provided on the chipset IPQ5018 */
+       /* This resource is optional */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
diff --git a/target/linux/generic/backport-6.6/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch b/target/linux/generic/backport-6.6/721-v6.7-net-phy-aquantia-drop-wrong-endianness-conversion-fo.patch
new file mode 100644 (file)
index 0000000..d32a7ed
--- /dev/null
@@ -0,0 +1,92 @@
+From 7edce370d87a23e8ed46af5b76a9fef1e341b67b Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 28 Nov 2023 14:59:28 +0100
+Subject: [PATCH] net: phy: aquantia: drop wrong endianness conversion for addr
+ and CRC
+
+On further testing on BE target with kernel test robot, it was notice
+that the endianness conversion for addr and CRC in fw_load_memory was
+wrong.
+
+Drop the cpu_to_le32 conversion for addr load as it's not needed.
+
+Use get_unaligned_le32 instead of get_unaligned for FW data word load to
+correctly convert data in the correct order to follow system endian.
+
+Also drop the cpu_to_be32 for CRC calculation as it's wrong and would
+cause different CRC on BE system.
+The loaded word is swapped internally and MAILBOX calculates the CRC on
+the swapped word. To correctly calculate the CRC to be later matched
+with the one from MAILBOX, use an u8 struct and swap the word there to
+keep the same order on both LE and BE for crc_ccitt_false function.
+Also add additional comments on how the CRC verification for the loaded
+section works.
+
+CRC is calculated as we load the section and verified with the MAILBOX
+only after the entire section is loaded to skip additional slowdown by
+loop the section data again.
+
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202311210414.sEJZjlcD-lkp@intel.com/
+Fixes: e93984ebc1c8 ("net: phy: aquantia: add firmware load support")
+Tested-by: Robert Marko <robimarko@gmail.com> # ipq8072 LE device
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Link: https://lore.kernel.org/r/20231128135928.9841-1-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/aquantia/aquantia_firmware.c | 24 ++++++++++++--------
+ 1 file changed, 14 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/phy/aquantia/aquantia_firmware.c
++++ b/drivers/net/phy/aquantia/aquantia_firmware.c
+@@ -93,9 +93,6 @@ static int aqr_fw_load_memory(struct phy
+       u16 crc = 0, up_crc;
+       size_t pos;
+-      /* PHY expect addr in LE */
+-      addr = (__force u32)cpu_to_le32(addr);
+-
+       phy_write_mmd(phydev, MDIO_MMD_VEND1,
+                     VEND1_GLOBAL_MAILBOX_INTERFACE1,
+                     VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
+@@ -110,10 +107,11 @@ static int aqr_fw_load_memory(struct phy
+        * If a firmware that is not word aligned is found, please report upstream.
+        */
+       for (pos = 0; pos < len; pos += sizeof(u32)) {
++              u8 crc_data[4];
+               u32 word;
+               /* FW data is always stored in little-endian */
+-              word = get_unaligned((const u32 *)(data + pos));
++              word = get_unaligned_le32((const u32 *)(data + pos));
+               phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
+                             VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
+@@ -124,15 +122,21 @@ static int aqr_fw_load_memory(struct phy
+                             VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |
+                             VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);
+-              /* calculate CRC as we load data to the mailbox.
+-               * We convert word to big-endian as PHY is BE and mailbox will
+-               * return a BE CRC.
++              /* Word is swapped internally and MAILBOX CRC is calculated
++               * using big-endian order. Mimic what the PHY does to have a
++               * matching CRC...
+                */
+-              word = (__force u32)cpu_to_be32(word);
+-              crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word));
+-      }
++              crc_data[0] = word >> 24;
++              crc_data[1] = word >> 16;
++              crc_data[2] = word >> 8;
++              crc_data[3] = word;
++              /* ...calculate CRC as we load data... */
++              crc = crc_ccitt_false(crc, crc_data, sizeof(crc_data));
++      }
++      /* ...gets CRC from MAILBOX after we have loaded the entire section... */
+       up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);
++      /* ...and make sure it does match our calculated CRC */
+       if (crc != up_crc) {
+               phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
+                          crc, up_crc);
diff --git a/target/linux/generic/backport-6.6/722-v6.10-dt-bindings-arm-qcom-ids-Add-SoC-ID-for-IPQ5321.patch b/target/linux/generic/backport-6.6/722-v6.10-dt-bindings-arm-qcom-ids-Add-SoC-ID-for-IPQ5321.patch
new file mode 100644 (file)
index 0000000..b05648a
--- /dev/null
@@ -0,0 +1,28 @@
+From 27c42e925323b975a64429e313b0cf5c0c02a411 Mon Sep 17 00:00:00 2001
+From: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
+Date: Mon, 25 Mar 2024 21:19:48 +0530
+Subject: dt-bindings: arm: qcom,ids: Add SoC ID for IPQ5321
+
+Add the ID for the Qualcomm IPQ5321 SoC.
+
+Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Reviewed-by: Mukesh Ojha <quic_mojha@quicinc.com>
+Signed-off-by: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
+Link: https://lore.kernel.org/r/20240325-ipq5321-sku-support-v2-1-f30ce244732f@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ include/dt-bindings/arm/qcom,ids.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+(limited to 'include/dt-bindings/arm/qcom,ids.h')
+
+--- a/include/dt-bindings/arm/qcom,ids.h
++++ b/include/dt-bindings/arm/qcom,ids.h
+@@ -260,6 +260,7 @@
+ #define QCOM_ID_IPQ5312                       594
+ #define QCOM_ID_IPQ5302                       595
+ #define QCOM_ID_IPQ5300                       624
++#define QCOM_ID_IPQ5321                       650
+ /*
+  * The board type and revision information, used by Qualcomm bootloaders and
diff --git a/target/linux/generic/backport-6.6/734-v6.8-net-phy-bcm54612e-add-suspend-resume.patch b/target/linux/generic/backport-6.6/734-v6.8-net-phy-bcm54612e-add-suspend-resume.patch
new file mode 100644 (file)
index 0000000..28524a3
--- /dev/null
@@ -0,0 +1,27 @@
+From 380b50ae3a04222334a3779b3787eba844b1177f Mon Sep 17 00:00:00 2001
+From: Marco von Rosenberg <marcovr@selfnet.de>
+Date: Thu, 16 Nov 2023 20:32:31 +0100
+Subject: net: phy: broadcom: Wire suspend/resume for BCM54612E
+
+The BCM54612E ethernet PHY supports IDDQ-SR.
+Therefore wire-up the suspend and resume callbacks
+to point to bcm54xx_suspend() and bcm54xx_resume().
+
+Signed-off-by: Marco von Rosenberg <marcovr@selfnet.de>
+Acked-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/broadcom.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -1001,6 +1001,8 @@ static struct phy_driver broadcom_driver
+       .config_intr    = bcm_phy_config_intr,
+       .handle_interrupt = bcm_phy_handle_interrupt,
+       .link_change_notify     = bcm54xx_link_change_notify,
++      .suspend        = bcm54xx_suspend,
++      .resume         = bcm54xx_resume,
+ }, {
+       .phy_id         = PHY_ID_BCM5421,
+       .phy_id_mask    = 0xfffffff0,
diff --git a/target/linux/generic/backport-6.6/740-v6.10-net-stmmac-dwmac-ipq806x-account-for-rgmii-txid-rxid.patch b/target/linux/generic/backport-6.6/740-v6.10-net-stmmac-dwmac-ipq806x-account-for-rgmii-txid-rxid.patch
new file mode 100644 (file)
index 0000000..ab8e9ff
--- /dev/null
@@ -0,0 +1,68 @@
+From abb45a2477f533cd4aab3085defdff131e2e8c4f Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 6 May 2024 14:32:46 +0200
+Subject: [PATCH] net: stmmac: dwmac-ipq806x: account for rgmii-txid/rxid/id
+ phy-mode
+
+Currently the ipq806x dwmac driver is almost always used attached to the
+CPU port of a switch and phy-mode was always set to "rgmii" or "sgmii".
+
+Some device came up with a special configuration where the PHY is
+directly attached to the GMAC port and in those case phy-mode needs to
+be set to "rgmii-id" to make the PHY correctly work and receive packets.
+
+Since the driver supports only "rgmii" and "sgmii" mode, when "rgmii-id"
+(or variants) mode is set, the mode is rejected and probe fails.
+
+Add support also for these phy-modes to correctly setup PHYs that requires
+delay applied to tx/rx.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
+@@ -171,6 +171,9 @@ static int ipq806x_gmac_set_speed(struct
+       switch (gmac->phy_mode) {
+       case PHY_INTERFACE_MODE_RGMII:
++      case PHY_INTERFACE_MODE_RGMII_ID:
++      case PHY_INTERFACE_MODE_RGMII_RXID:
++      case PHY_INTERFACE_MODE_RGMII_TXID:
+               div = get_clk_div_rgmii(gmac, speed);
+               clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) |
+                          NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id);
+@@ -412,6 +415,9 @@ static int ipq806x_gmac_probe(struct pla
+       val |= NSS_COMMON_GMAC_CTL_CSYS_REQ;
+       switch (gmac->phy_mode) {
+       case PHY_INTERFACE_MODE_RGMII:
++      case PHY_INTERFACE_MODE_RGMII_ID:
++      case PHY_INTERFACE_MODE_RGMII_RXID:
++      case PHY_INTERFACE_MODE_RGMII_TXID:
+               val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL;
+               break;
+       case PHY_INTERFACE_MODE_SGMII:
+@@ -427,6 +433,9 @@ static int ipq806x_gmac_probe(struct pla
+       val &= ~(1 << NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id));
+       switch (gmac->phy_mode) {
+       case PHY_INTERFACE_MODE_RGMII:
++      case PHY_INTERFACE_MODE_RGMII_ID:
++      case PHY_INTERFACE_MODE_RGMII_RXID:
++      case PHY_INTERFACE_MODE_RGMII_TXID:
+               val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) <<
+                       NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
+               break;
+@@ -444,6 +453,9 @@ static int ipq806x_gmac_probe(struct pla
+       val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id);
+       switch (gmac->phy_mode) {
+       case PHY_INTERFACE_MODE_RGMII:
++      case PHY_INTERFACE_MODE_RGMII_ID:
++      case PHY_INTERFACE_MODE_RGMII_RXID:
++      case PHY_INTERFACE_MODE_RGMII_TXID:
+               val |= NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) |
+                       NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id);
+               break;
diff --git a/target/linux/generic/backport-6.6/751-01-STABLE-net-ethernet-mediatek-split-tx-and-rx-fields-in-mtk_.patch b/target/linux/generic/backport-6.6/751-01-STABLE-net-ethernet-mediatek-split-tx-and-rx-fields-in-mtk_.patch
new file mode 100644 (file)
index 0000000..22aceec
--- /dev/null
@@ -0,0 +1,605 @@
+From 5d0fad48d2dec175ecb999974b94203c577973ef Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Wed, 8 May 2024 11:43:34 +0100
+Subject: [PATCH] net: ethernet: mediatek: split tx and rx fields in
+ mtk_soc_data struct
+
+Split tx and rx fields in mtk_soc_data struct. This is a preliminary
+patch to roll back to ADMAv1 for MT7986 and MT7981 SoC in order to fix a
+hw hang if the device receives a corrupted packet when using ADMAv2.0.
+
+Fixes: 197c9e9b17b1 ("net: ethernet: mtk_eth_soc: introduce support for mt7986 chipset")
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Link: https://lore.kernel.org/r/70a799b1f060ec2f57883e88ccb420ac0fb0abb5.1715164770.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 210 ++++++++++++--------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h |  29 +--
+ 2 files changed, 139 insertions(+), 100 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1138,7 +1138,7 @@ static int mtk_init_fq_dma(struct mtk_et
+               eth->scratch_ring = eth->sram_base;
+       else
+               eth->scratch_ring = dma_alloc_coherent(eth->dma_dev,
+-                                                     cnt * soc->txrx.txd_size,
++                                                     cnt * soc->tx.desc_size,
+                                                      &eth->phy_scratch_ring,
+                                                      GFP_KERNEL);
+       if (unlikely(!eth->scratch_ring))
+@@ -1154,16 +1154,16 @@ static int mtk_init_fq_dma(struct mtk_et
+       if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
+               return -ENOMEM;
+-      phy_ring_tail = eth->phy_scratch_ring + soc->txrx.txd_size * (cnt - 1);
++      phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1);
+       for (i = 0; i < cnt; i++) {
+               struct mtk_tx_dma_v2 *txd;
+-              txd = eth->scratch_ring + i * soc->txrx.txd_size;
++              txd = eth->scratch_ring + i * soc->tx.desc_size;
+               txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
+               if (i < cnt - 1)
+                       txd->txd2 = eth->phy_scratch_ring +
+-                                  (i + 1) * soc->txrx.txd_size;
++                                  (i + 1) * soc->tx.desc_size;
+               txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
+               txd->txd4 = 0;
+@@ -1412,7 +1412,7 @@ static int mtk_tx_map(struct sk_buff *sk
+       if (itxd == ring->last_free)
+               return -ENOMEM;
+-      itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
++      itx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_size);
+       memset(itx_buf, 0, sizeof(*itx_buf));
+       txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size,
+@@ -1453,7 +1453,7 @@ static int mtk_tx_map(struct sk_buff *sk
+                       memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info));
+                       txd_info.size = min_t(unsigned int, frag_size,
+-                                            soc->txrx.dma_max_len);
++                                            soc->tx.dma_max_len);
+                       txd_info.qid = queue;
+                       txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 &&
+                                       !(frag_size - txd_info.size);
+@@ -1466,7 +1466,7 @@ static int mtk_tx_map(struct sk_buff *sk
+                       mtk_tx_set_dma_desc(dev, txd, &txd_info);
+                       tx_buf = mtk_desc_to_tx_buf(ring, txd,
+-                                                  soc->txrx.txd_size);
++                                                  soc->tx.desc_size);
+                       if (new_desc)
+                               memset(tx_buf, 0, sizeof(*tx_buf));
+                       tx_buf->data = (void *)MTK_DMA_DUMMY_DESC;
+@@ -1509,7 +1509,7 @@ static int mtk_tx_map(struct sk_buff *sk
+       } else {
+               int next_idx;
+-              next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->txrx.txd_size),
++              next_idx = NEXT_DESP_IDX(txd_to_idx(ring, txd, soc->tx.desc_size),
+                                        ring->dma_size);
+               mtk_w32(eth, next_idx, MT7628_TX_CTX_IDX0);
+       }
+@@ -1518,7 +1518,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ err_dma:
+       do {
+-              tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->txrx.txd_size);
++              tx_buf = mtk_desc_to_tx_buf(ring, itxd, soc->tx.desc_size);
+               /* unmap dma */
+               mtk_tx_unmap(eth, tx_buf, NULL, false);
+@@ -1543,7 +1543,7 @@ static int mtk_cal_txd_req(struct mtk_et
+               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+                       frag = &skb_shinfo(skb)->frags[i];
+                       nfrags += DIV_ROUND_UP(skb_frag_size(frag),
+-                                             eth->soc->txrx.dma_max_len);
++                                             eth->soc->tx.dma_max_len);
+               }
+       } else {
+               nfrags += skb_shinfo(skb)->nr_frags;
+@@ -1650,7 +1650,7 @@ static struct mtk_rx_ring *mtk_get_rx_ri
+               ring = &eth->rx_ring[i];
+               idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
+-              rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
++              rxd = ring->dma + idx * eth->soc->rx.desc_size;
+               if (rxd->rxd2 & RX_DMA_DONE) {
+                       ring->calc_idx_update = true;
+                       return ring;
+@@ -1818,7 +1818,7 @@ static int mtk_xdp_submit_frame(struct m
+       }
+       htxd = txd;
+-      tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size);
++      tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->tx.desc_size);
+       memset(tx_buf, 0, sizeof(*tx_buf));
+       htx_buf = tx_buf;
+@@ -1837,7 +1837,7 @@ static int mtk_xdp_submit_frame(struct m
+                               goto unmap;
+                       tx_buf = mtk_desc_to_tx_buf(ring, txd,
+-                                                  soc->txrx.txd_size);
++                                                  soc->tx.desc_size);
+                       memset(tx_buf, 0, sizeof(*tx_buf));
+                       n_desc++;
+               }
+@@ -1875,7 +1875,7 @@ static int mtk_xdp_submit_frame(struct m
+       } else {
+               int idx;
+-              idx = txd_to_idx(ring, txd, soc->txrx.txd_size);
++              idx = txd_to_idx(ring, txd, soc->tx.desc_size);
+               mtk_w32(eth, NEXT_DESP_IDX(idx, ring->dma_size),
+                       MT7628_TX_CTX_IDX0);
+       }
+@@ -1886,7 +1886,7 @@ static int mtk_xdp_submit_frame(struct m
+ unmap:
+       while (htxd != txd) {
+-              tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->txrx.txd_size);
++              tx_buf = mtk_desc_to_tx_buf(ring, htxd, soc->tx.desc_size);
+               mtk_tx_unmap(eth, tx_buf, NULL, false);
+               htxd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU;
+@@ -2017,7 +2017,7 @@ static int mtk_poll_rx(struct napi_struc
+                       goto rx_done;
+               idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
+-              rxd = ring->dma + idx * eth->soc->txrx.rxd_size;
++              rxd = ring->dma + idx * eth->soc->rx.desc_size;
+               data = ring->data[idx];
+               if (!mtk_rx_get_desc(eth, &trxd, rxd))
+@@ -2152,7 +2152,7 @@ static int mtk_poll_rx(struct napi_struc
+                       rxdcsum = &trxd.rxd4;
+               }
+-              if (*rxdcsum & eth->soc->txrx.rx_dma_l4_valid)
++              if (*rxdcsum & eth->soc->rx.dma_l4_valid)
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+               else
+                       skb_checksum_none_assert(skb);
+@@ -2276,7 +2276,7 @@ static int mtk_poll_tx_qdma(struct mtk_e
+                       break;
+               tx_buf = mtk_desc_to_tx_buf(ring, desc,
+-                                          eth->soc->txrx.txd_size);
++                                          eth->soc->tx.desc_size);
+               if (!tx_buf->data)
+                       break;
+@@ -2327,7 +2327,7 @@ static int mtk_poll_tx_pdma(struct mtk_e
+               }
+               mtk_tx_unmap(eth, tx_buf, &bq, true);
+-              desc = ring->dma + cpu * eth->soc->txrx.txd_size;
++              desc = ring->dma + cpu * eth->soc->tx.desc_size;
+               ring->last_free = desc;
+               atomic_inc(&ring->free_count);
+@@ -2417,7 +2417,7 @@ static int mtk_napi_rx(struct napi_struc
+       do {
+               int rx_done;
+-              mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask,
++              mtk_w32(eth, eth->soc->rx.irq_done_mask,
+                       reg_map->pdma.irq_status);
+               rx_done = mtk_poll_rx(napi, budget - rx_done_total, eth);
+               rx_done_total += rx_done;
+@@ -2433,10 +2433,10 @@ static int mtk_napi_rx(struct napi_struc
+                       return budget;
+       } while (mtk_r32(eth, reg_map->pdma.irq_status) &
+-               eth->soc->txrx.rx_irq_done_mask);
++               eth->soc->rx.irq_done_mask);
+       if (napi_complete_done(napi, rx_done_total))
+-              mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
++              mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask);
+       return rx_done_total;
+ }
+@@ -2445,7 +2445,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+ {
+       const struct mtk_soc_data *soc = eth->soc;
+       struct mtk_tx_ring *ring = &eth->tx_ring;
+-      int i, sz = soc->txrx.txd_size;
++      int i, sz = soc->tx.desc_size;
+       struct mtk_tx_dma_v2 *txd;
+       int ring_size;
+       u32 ofs, val;
+@@ -2568,14 +2568,14 @@ static void mtk_tx_clean(struct mtk_eth
+       }
+       if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && ring->dma) {
+               dma_free_coherent(eth->dma_dev,
+-                                ring->dma_size * soc->txrx.txd_size,
++                                ring->dma_size * soc->tx.desc_size,
+                                 ring->dma, ring->phys);
+               ring->dma = NULL;
+       }
+       if (ring->dma_pdma) {
+               dma_free_coherent(eth->dma_dev,
+-                                ring->dma_size * soc->txrx.txd_size,
++                                ring->dma_size * soc->tx.desc_size,
+                                 ring->dma_pdma, ring->phys_pdma);
+               ring->dma_pdma = NULL;
+       }
+@@ -2630,15 +2630,15 @@ static int mtk_rx_alloc(struct mtk_eth *
+       if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM) ||
+           rx_flag != MTK_RX_FLAGS_NORMAL) {
+               ring->dma = dma_alloc_coherent(eth->dma_dev,
+-                                             rx_dma_size * eth->soc->txrx.rxd_size,
+-                                             &ring->phys, GFP_KERNEL);
++                              rx_dma_size * eth->soc->rx.desc_size,
++                              &ring->phys, GFP_KERNEL);
+       } else {
+               struct mtk_tx_ring *tx_ring = &eth->tx_ring;
+               ring->dma = tx_ring->dma + tx_ring_size *
+-                          eth->soc->txrx.txd_size * (ring_no + 1);
++                          eth->soc->tx.desc_size * (ring_no + 1);
+               ring->phys = tx_ring->phys + tx_ring_size *
+-                           eth->soc->txrx.txd_size * (ring_no + 1);
++                           eth->soc->tx.desc_size * (ring_no + 1);
+       }
+       if (!ring->dma)
+@@ -2649,7 +2649,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+               dma_addr_t dma_addr;
+               void *data;
+-              rxd = ring->dma + i * eth->soc->txrx.rxd_size;
++              rxd = ring->dma + i * eth->soc->rx.desc_size;
+               if (ring->page_pool) {
+                       data = mtk_page_pool_get_buff(ring->page_pool,
+                                                     &dma_addr, GFP_KERNEL);
+@@ -2740,7 +2740,7 @@ static void mtk_rx_clean(struct mtk_eth
+                       if (!ring->data[i])
+                               continue;
+-                      rxd = ring->dma + i * eth->soc->txrx.rxd_size;
++                      rxd = ring->dma + i * eth->soc->rx.desc_size;
+                       if (!rxd->rxd1)
+                               continue;
+@@ -2757,7 +2757,7 @@ static void mtk_rx_clean(struct mtk_eth
+       if (!in_sram && ring->dma) {
+               dma_free_coherent(eth->dma_dev,
+-                                ring->dma_size * eth->soc->txrx.rxd_size,
++                                ring->dma_size * eth->soc->rx.desc_size,
+                                 ring->dma, ring->phys);
+               ring->dma = NULL;
+       }
+@@ -3120,7 +3120,7 @@ static void mtk_dma_free(struct mtk_eth
+                       netdev_reset_queue(eth->netdev[i]);
+       if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && eth->scratch_ring) {
+               dma_free_coherent(eth->dma_dev,
+-                                MTK_QDMA_RING_SIZE * soc->txrx.txd_size,
++                                MTK_QDMA_RING_SIZE * soc->tx.desc_size,
+                                 eth->scratch_ring, eth->phy_scratch_ring);
+               eth->scratch_ring = NULL;
+               eth->phy_scratch_ring = 0;
+@@ -3170,7 +3170,7 @@ static irqreturn_t mtk_handle_irq_rx(int
+       eth->rx_events++;
+       if (likely(napi_schedule_prep(&eth->rx_napi))) {
+-              mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
++              mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
+               __napi_schedule(&eth->rx_napi);
+       }
+@@ -3196,9 +3196,9 @@ static irqreturn_t mtk_handle_irq(int ir
+       const struct mtk_reg_map *reg_map = eth->soc->reg_map;
+       if (mtk_r32(eth, reg_map->pdma.irq_mask) &
+-          eth->soc->txrx.rx_irq_done_mask) {
++          eth->soc->rx.irq_done_mask) {
+               if (mtk_r32(eth, reg_map->pdma.irq_status) &
+-                  eth->soc->txrx.rx_irq_done_mask)
++                  eth->soc->rx.irq_done_mask)
+                       mtk_handle_irq_rx(irq, _eth);
+       }
+       if (mtk_r32(eth, reg_map->tx_irq_mask) & MTK_TX_DONE_INT) {
+@@ -3216,10 +3216,10 @@ static void mtk_poll_controller(struct n
+       struct mtk_eth *eth = mac->hw;
+       mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+-      mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
++      mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
+       mtk_handle_irq_rx(eth->irq[2], dev);
+       mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+-      mtk_rx_irq_enable(eth, eth->soc->txrx.rx_irq_done_mask);
++      mtk_rx_irq_enable(eth, eth->soc->rx.irq_done_mask);
+ }
+ #endif
+@@ -3383,7 +3383,7 @@ static int mtk_open(struct net_device *d
+               napi_enable(&eth->tx_napi);
+               napi_enable(&eth->rx_napi);
+               mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+-              mtk_rx_irq_enable(eth, soc->txrx.rx_irq_done_mask);
++              mtk_rx_irq_enable(eth, soc->rx.irq_done_mask);
+               refcount_set(&eth->dma_refcnt, 1);
+       }
+       else
+@@ -3467,7 +3467,7 @@ static int mtk_stop(struct net_device *d
+       mtk_gdm_config(eth, MTK_GDMA_DROP_ALL);
+       mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+-      mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask);
++      mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
+       napi_disable(&eth->tx_napi);
+       napi_disable(&eth->rx_napi);
+@@ -3943,9 +3943,9 @@ static int mtk_hw_init(struct mtk_eth *e
+       /* FE int grouping */
+       mtk_w32(eth, MTK_TX_DONE_INT, reg_map->pdma.int_grp);
+-      mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->pdma.int_grp + 4);
++      mtk_w32(eth, eth->soc->rx.irq_done_mask, reg_map->pdma.int_grp + 4);
+       mtk_w32(eth, MTK_TX_DONE_INT, reg_map->qdma.int_grp);
+-      mtk_w32(eth, eth->soc->txrx.rx_irq_done_mask, reg_map->qdma.int_grp + 4);
++      mtk_w32(eth, eth->soc->rx.irq_done_mask, reg_map->qdma.int_grp + 4);
+       mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
+       if (mtk_is_netsys_v3_or_greater(eth)) {
+@@ -5037,11 +5037,15 @@ static const struct mtk_soc_data mt2701_
+       .required_clks = MT7623_CLKS_BITMAP,
+       .required_pctl = true,
+       .version = 1,
+-      .txrx = {
+-              .txd_size = sizeof(struct mtk_tx_dma),
+-              .rxd_size = sizeof(struct mtk_rx_dma),
+-              .rx_irq_done_mask = MTK_RX_DONE_INT,
+-              .rx_dma_l4_valid = RX_DMA_L4_VALID,
++      .tx = {
++              .desc_size = sizeof(struct mtk_tx_dma),
++              .dma_max_len = MTK_TX_DMA_BUF_LEN,
++              .dma_len_offset = 16,
++      },
++      .rx = {
++              .desc_size = sizeof(struct mtk_rx_dma),
++              .irq_done_mask = MTK_RX_DONE_INT,
++              .dma_l4_valid = RX_DMA_L4_VALID,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5057,11 +5061,15 @@ static const struct mtk_soc_data mt7621_
+       .offload_version = 1,
+       .hash_offset = 2,
+       .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
+-      .txrx = {
+-              .txd_size = sizeof(struct mtk_tx_dma),
+-              .rxd_size = sizeof(struct mtk_rx_dma),
+-              .rx_irq_done_mask = MTK_RX_DONE_INT,
+-              .rx_dma_l4_valid = RX_DMA_L4_VALID,
++      .tx = {
++              .desc_size = sizeof(struct mtk_tx_dma),
++              .dma_max_len = MTK_TX_DMA_BUF_LEN,
++              .dma_len_offset = 16,
++      },
++      .rx = {
++              .desc_size = sizeof(struct mtk_rx_dma),
++              .irq_done_mask = MTK_RX_DONE_INT,
++              .dma_l4_valid = RX_DMA_L4_VALID,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5079,11 +5087,15 @@ static const struct mtk_soc_data mt7622_
+       .hash_offset = 2,
+       .has_accounting = true,
+       .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
+-      .txrx = {
+-              .txd_size = sizeof(struct mtk_tx_dma),
+-              .rxd_size = sizeof(struct mtk_rx_dma),
+-              .rx_irq_done_mask = MTK_RX_DONE_INT,
+-              .rx_dma_l4_valid = RX_DMA_L4_VALID,
++      .tx = {
++              .desc_size = sizeof(struct mtk_tx_dma),
++              .dma_max_len = MTK_TX_DMA_BUF_LEN,
++              .dma_len_offset = 16,
++      },
++      .rx = {
++              .desc_size = sizeof(struct mtk_rx_dma),
++              .irq_done_mask = MTK_RX_DONE_INT,
++              .dma_l4_valid = RX_DMA_L4_VALID,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5100,11 +5112,15 @@ static const struct mtk_soc_data mt7623_
+       .hash_offset = 2,
+       .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
+       .disable_pll_modes = true,
+-      .txrx = {
+-              .txd_size = sizeof(struct mtk_tx_dma),
+-              .rxd_size = sizeof(struct mtk_rx_dma),
+-              .rx_irq_done_mask = MTK_RX_DONE_INT,
+-              .rx_dma_l4_valid = RX_DMA_L4_VALID,
++      .tx = {
++              .desc_size = sizeof(struct mtk_tx_dma),
++              .dma_max_len = MTK_TX_DMA_BUF_LEN,
++              .dma_len_offset = 16,
++      },
++      .rx = {
++              .desc_size = sizeof(struct mtk_rx_dma),
++              .irq_done_mask = MTK_RX_DONE_INT,
++              .dma_l4_valid = RX_DMA_L4_VALID,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5119,11 +5135,15 @@ static const struct mtk_soc_data mt7629_
+       .required_pctl = false,
+       .has_accounting = true,
+       .version = 1,
+-      .txrx = {
+-              .txd_size = sizeof(struct mtk_tx_dma),
+-              .rxd_size = sizeof(struct mtk_rx_dma),
+-              .rx_irq_done_mask = MTK_RX_DONE_INT,
+-              .rx_dma_l4_valid = RX_DMA_L4_VALID,
++      .tx = {
++              .desc_size = sizeof(struct mtk_tx_dma),
++              .dma_max_len = MTK_TX_DMA_BUF_LEN,
++              .dma_len_offset = 16,
++      },
++      .rx = {
++              .desc_size = sizeof(struct mtk_rx_dma),
++              .irq_done_mask = MTK_RX_DONE_INT,
++              .dma_l4_valid = RX_DMA_L4_VALID,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5141,11 +5161,15 @@ static const struct mtk_soc_data mt7981_
+       .hash_offset = 4,
+       .has_accounting = true,
+       .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
+-      .txrx = {
+-              .txd_size = sizeof(struct mtk_tx_dma_v2),
+-              .rxd_size = sizeof(struct mtk_rx_dma_v2),
+-              .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
+-              .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
++      .tx = {
++              .desc_size = sizeof(struct mtk_tx_dma_v2),
++              .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
++              .dma_len_offset = 8,
++      },
++      .rx = {
++              .desc_size = sizeof(struct mtk_rx_dma_v2),
++              .irq_done_mask = MTK_RX_DONE_INT_V2,
++              .dma_l4_valid = RX_DMA_L4_VALID_V2,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+               .dma_len_offset = 8,
+       },
+@@ -5163,11 +5187,15 @@ static const struct mtk_soc_data mt7986_
+       .hash_offset = 4,
+       .has_accounting = true,
+       .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
+-      .txrx = {
+-              .txd_size = sizeof(struct mtk_tx_dma_v2),
+-              .rxd_size = sizeof(struct mtk_rx_dma_v2),
+-              .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
+-              .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
++      .tx = {
++              .desc_size = sizeof(struct mtk_tx_dma_v2),
++              .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
++              .dma_len_offset = 8,
++      },
++      .rx = {
++              .desc_size = sizeof(struct mtk_rx_dma_v2),
++              .irq_done_mask = MTK_RX_DONE_INT_V2,
++              .dma_l4_valid = RX_DMA_L4_VALID_V2,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+               .dma_len_offset = 8,
+       },
+@@ -5185,11 +5213,15 @@ static const struct mtk_soc_data mt7988_
+       .hash_offset = 4,
+       .has_accounting = true,
+       .foe_entry_size = MTK_FOE_ENTRY_V3_SIZE,
+-      .txrx = {
+-              .txd_size = sizeof(struct mtk_tx_dma_v2),
+-              .rxd_size = sizeof(struct mtk_rx_dma_v2),
+-              .rx_irq_done_mask = MTK_RX_DONE_INT_V2,
+-              .rx_dma_l4_valid = RX_DMA_L4_VALID_V2,
++      .tx = {
++              .desc_size = sizeof(struct mtk_tx_dma_v2),
++              .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
++              .dma_len_offset = 8,
++      },
++      .rx = {
++              .desc_size = sizeof(struct mtk_rx_dma_v2),
++              .irq_done_mask = MTK_RX_DONE_INT_V2,
++              .dma_l4_valid = RX_DMA_L4_VALID_V2,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+               .dma_len_offset = 8,
+       },
+@@ -5202,11 +5234,15 @@ static const struct mtk_soc_data rt5350_
+       .required_clks = MT7628_CLKS_BITMAP,
+       .required_pctl = false,
+       .version = 1,
+-      .txrx = {
+-              .txd_size = sizeof(struct mtk_tx_dma),
+-              .rxd_size = sizeof(struct mtk_rx_dma),
+-              .rx_irq_done_mask = MTK_RX_DONE_INT,
+-              .rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA,
++      .tx = {
++              .desc_size = sizeof(struct mtk_tx_dma),
++              .dma_max_len = MTK_TX_DMA_BUF_LEN,
++              .dma_len_offset = 16,
++      },
++      .rx = {
++              .desc_size = sizeof(struct mtk_rx_dma),
++              .irq_done_mask = MTK_RX_DONE_INT,
++              .dma_l4_valid = RX_DMA_L4_VALID_PDMA,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -327,8 +327,8 @@
+ /* QDMA descriptor txd3 */
+ #define TX_DMA_OWNER_CPU      BIT(31)
+ #define TX_DMA_LS0            BIT(30)
+-#define TX_DMA_PLEN0(x)               (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
+-#define TX_DMA_PLEN1(x)               ((x) & eth->soc->txrx.dma_max_len)
++#define TX_DMA_PLEN0(x)               (((x) & eth->soc->tx.dma_max_len) << eth->soc->tx.dma_len_offset)
++#define TX_DMA_PLEN1(x)               ((x) & eth->soc->tx.dma_max_len)
+ #define TX_DMA_SWC            BIT(14)
+ #define TX_DMA_PQID           GENMASK(3, 0)
+ #define TX_DMA_ADDR64_MASK    GENMASK(3, 0)
+@@ -348,8 +348,8 @@
+ /* QDMA descriptor rxd2 */
+ #define RX_DMA_DONE           BIT(31)
+ #define RX_DMA_LSO            BIT(30)
+-#define RX_DMA_PREP_PLEN0(x)  (((x) & eth->soc->txrx.dma_max_len) << eth->soc->txrx.dma_len_offset)
+-#define RX_DMA_GET_PLEN0(x)   (((x) >> eth->soc->txrx.dma_len_offset) & eth->soc->txrx.dma_max_len)
++#define RX_DMA_PREP_PLEN0(x)  (((x) & eth->soc->rx.dma_max_len) << eth->soc->rx.dma_len_offset)
++#define RX_DMA_GET_PLEN0(x)   (((x) >> eth->soc->rx.dma_len_offset) & eth->soc->rx.dma_max_len)
+ #define RX_DMA_VTAG           BIT(15)
+ #define RX_DMA_ADDR64_MASK    GENMASK(3, 0)
+ #if IS_ENABLED(CONFIG_64BIT)
+@@ -1153,10 +1153,9 @@ struct mtk_reg_map {
+  * @foe_entry_size            Foe table entry size.
+  * @has_accounting            Bool indicating support for accounting of
+  *                            offloaded flows.
+- * @txd_size                  Tx DMA descriptor size.
+- * @rxd_size                  Rx DMA descriptor size.
+- * @rx_irq_done_mask          Rx irq done register mask.
+- * @rx_dma_l4_valid           Rx DMA valid register mask.
++ * @desc_size                 Tx/Rx DMA descriptor size.
++ * @irq_done_mask             Rx irq done register mask.
++ * @dma_l4_valid              Rx DMA valid register mask.
+  * @dma_max_len                       Max DMA tx/rx buffer length.
+  * @dma_len_offset            Tx/Rx DMA length field offset.
+  */
+@@ -1174,13 +1173,17 @@ struct mtk_soc_data {
+       bool            has_accounting;
+       bool            disable_pll_modes;
+       struct {
+-              u32     txd_size;
+-              u32     rxd_size;
+-              u32     rx_irq_done_mask;
+-              u32     rx_dma_l4_valid;
++              u32     desc_size;
+               u32     dma_max_len;
+               u32     dma_len_offset;
+-      } txrx;
++      } tx;
++      struct {
++              u32     desc_size;
++              u32     irq_done_mask;
++              u32     dma_l4_valid;
++              u32     dma_max_len;
++              u32     dma_len_offset;
++      } rx;
+ };
+ #define MTK_DMA_MONITOR_TIMEOUT               msecs_to_jiffies(1000)
diff --git a/target/linux/generic/backport-6.6/751-02-STABLE-net-ethernet-mediatek-use-QDMA-instead-of-ADMAv2-on-.patch b/target/linux/generic/backport-6.6/751-02-STABLE-net-ethernet-mediatek-use-QDMA-instead-of-ADMAv2-on-.patch
new file mode 100644 (file)
index 0000000..e71ff09
--- /dev/null
@@ -0,0 +1,128 @@
+From 4d572e867bdb372bb4add39a0fa495c6a9c9a8da Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 8 May 2024 11:43:56 +0100
+Subject: [PATCH] net: ethernet: mediatek: use ADMAv1 instead of ADMAv2.0 on
+ MT7981 and MT7986
+
+ADMAv2.0 is plagued by RX hangs which can't easily detected and happen upon
+receival of a corrupted Ethernet frame.
+
+Use ADMAv1 instead which is also still present and usable, and doesn't
+suffer from that problem.
+
+Fixes: 197c9e9b17b1 ("net: ethernet: mtk_eth_soc: introduce support for mt7986 chipset")
+Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://lore.kernel.org/r/57cef74bbd0c243366ad1ff4221e3f72f437ec80.1715164770.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 46 ++++++++++-----------
+ 1 file changed, 23 insertions(+), 23 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -110,16 +110,16 @@ static const struct mtk_reg_map mt7986_r
+       .tx_irq_mask            = 0x461c,
+       .tx_irq_status          = 0x4618,
+       .pdma = {
+-              .rx_ptr         = 0x6100,
+-              .rx_cnt_cfg     = 0x6104,
+-              .pcrx_ptr       = 0x6108,
+-              .glo_cfg        = 0x6204,
+-              .rst_idx        = 0x6208,
+-              .delay_irq      = 0x620c,
+-              .irq_status     = 0x6220,
+-              .irq_mask       = 0x6228,
+-              .adma_rx_dbg0   = 0x6238,
+-              .int_grp        = 0x6250,
++              .rx_ptr         = 0x4100,
++              .rx_cnt_cfg     = 0x4104,
++              .pcrx_ptr       = 0x4108,
++              .glo_cfg        = 0x4204,
++              .rst_idx        = 0x4208,
++              .delay_irq      = 0x420c,
++              .irq_status     = 0x4220,
++              .irq_mask       = 0x4228,
++              .adma_rx_dbg0   = 0x4238,
++              .int_grp        = 0x4250,
+       },
+       .qdma = {
+               .qtx_cfg        = 0x4400,
+@@ -1106,7 +1106,7 @@ static bool mtk_rx_get_desc(struct mtk_e
+       rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
+       rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
+       rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
+-      if (mtk_is_netsys_v2_or_greater(eth)) {
++      if (mtk_is_netsys_v3_or_greater(eth)) {
+               rxd->rxd5 = READ_ONCE(dma_rxd->rxd5);
+               rxd->rxd6 = READ_ONCE(dma_rxd->rxd6);
+       }
+@@ -2024,7 +2024,7 @@ static int mtk_poll_rx(struct napi_struc
+                       break;
+               /* find out which mac the packet come from. values start at 1 */
+-              if (mtk_is_netsys_v2_or_greater(eth)) {
++              if (mtk_is_netsys_v3_or_greater(eth)) {
+                       u32 val = RX_DMA_GET_SPORT_V2(trxd.rxd5);
+                       switch (val) {
+@@ -2136,7 +2136,7 @@ static int mtk_poll_rx(struct napi_struc
+               skb->dev = netdev;
+               bytes += skb->len;
+-              if (mtk_is_netsys_v2_or_greater(eth)) {
++              if (mtk_is_netsys_v3_or_greater(eth)) {
+                       reason = FIELD_GET(MTK_RXD5_PPE_CPU_REASON, trxd.rxd5);
+                       hash = trxd.rxd5 & MTK_RXD5_FOE_ENTRY;
+                       if (hash != MTK_RXD5_FOE_ENTRY)
+@@ -2686,7 +2686,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+               rxd->rxd3 = 0;
+               rxd->rxd4 = 0;
+-              if (mtk_is_netsys_v2_or_greater(eth)) {
++              if (mtk_is_netsys_v3_or_greater(eth)) {
+                       rxd->rxd5 = 0;
+                       rxd->rxd6 = 0;
+                       rxd->rxd7 = 0;
+@@ -3889,7 +3889,7 @@ static int mtk_hw_init(struct mtk_eth *e
+       else
+               mtk_hw_reset(eth);
+-      if (mtk_is_netsys_v2_or_greater(eth)) {
++      if (mtk_is_netsys_v3_or_greater(eth)) {
+               /* Set FE to PDMAv2 if necessary */
+               val = mtk_r32(eth, MTK_FE_GLO_MISC);
+               mtk_w32(eth,  val | BIT(4), MTK_FE_GLO_MISC);
+@@ -5167,11 +5167,11 @@ static const struct mtk_soc_data mt7981_
+               .dma_len_offset = 8,
+       },
+       .rx = {
+-              .desc_size = sizeof(struct mtk_rx_dma_v2),
+-              .irq_done_mask = MTK_RX_DONE_INT_V2,
++              .desc_size = sizeof(struct mtk_rx_dma),
++              .irq_done_mask = MTK_RX_DONE_INT,
+               .dma_l4_valid = RX_DMA_L4_VALID_V2,
+-              .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+-              .dma_len_offset = 8,
++              .dma_max_len = MTK_TX_DMA_BUF_LEN,
++              .dma_len_offset = 16,
+       },
+ };
+@@ -5193,11 +5193,11 @@ static const struct mtk_soc_data mt7986_
+               .dma_len_offset = 8,
+       },
+       .rx = {
+-              .desc_size = sizeof(struct mtk_rx_dma_v2),
+-              .irq_done_mask = MTK_RX_DONE_INT_V2,
++              .desc_size = sizeof(struct mtk_rx_dma),
++              .irq_done_mask = MTK_RX_DONE_INT,
+               .dma_l4_valid = RX_DMA_L4_VALID_V2,
+-              .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+-              .dma_len_offset = 8,
++              .dma_max_len = MTK_TX_DMA_BUF_LEN,
++              .dma_len_offset = 16,
+       },
+ };
diff --git a/target/linux/generic/backport-6.6/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch b/target/linux/generic/backport-6.6/751-03-v6.4-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch
new file mode 100644 (file)
index 0000000..2689712
--- /dev/null
@@ -0,0 +1,334 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 23 Mar 2023 10:24:11 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: improve keeping track of
+ offloaded flows
+
+Unify tracking of L2 and L3 flows. Use the generic list field in struct
+mtk_foe_entry for tracking L2 subflows. Preparation for improving
+flow accounting support.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.c | 162 ++++++++++++------------
+ drivers/net/ethernet/mediatek/mtk_ppe.h |  15 +--
+ 2 files changed, 86 insertions(+), 91 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -477,42 +477,43 @@ int mtk_foe_entry_set_queue(struct mtk_e
+       return 0;
+ }
++static int
++mtk_flow_entry_match_len(struct mtk_eth *eth, struct mtk_foe_entry *entry)
++{
++      int type = mtk_get_ib1_pkt_type(eth, entry->ib1);
++
++      if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
++              return offsetof(struct mtk_foe_entry, ipv6._rsv);
++      else
++              return offsetof(struct mtk_foe_entry, ipv4.ib2);
++}
++
+ static bool
+ mtk_flow_entry_match(struct mtk_eth *eth, struct mtk_flow_entry *entry,
+-                   struct mtk_foe_entry *data)
++                   struct mtk_foe_entry *data, int len)
+ {
+-      int type, len;
+-
+       if ((data->ib1 ^ entry->data.ib1) & MTK_FOE_IB1_UDP)
+               return false;
+-      type = mtk_get_ib1_pkt_type(eth, entry->data.ib1);
+-      if (type > MTK_PPE_PKT_TYPE_IPV4_DSLITE)
+-              len = offsetof(struct mtk_foe_entry, ipv6._rsv);
+-      else
+-              len = offsetof(struct mtk_foe_entry, ipv4.ib2);
+-
+       return !memcmp(&entry->data.data, &data->data, len - 4);
+ }
+ static void
+-__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
++                    bool set_state)
+ {
+-      struct hlist_head *head;
+       struct hlist_node *tmp;
+       if (entry->type == MTK_FLOW_TYPE_L2) {
+               rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node,
+                                      mtk_flow_l2_ht_params);
+-              head = &entry->l2_flows;
+-              hlist_for_each_entry_safe(entry, tmp, head, l2_data.list)
+-                      __mtk_foe_entry_clear(ppe, entry);
++              hlist_for_each_entry_safe(entry, tmp, &entry->l2_flows, l2_list)
++                      __mtk_foe_entry_clear(ppe, entry, set_state);
+               return;
+       }
+-      hlist_del_init(&entry->list);
+-      if (entry->hash != 0xffff) {
++      if (entry->hash != 0xffff && set_state) {
+               struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash);
+               hwe->ib1 &= ~MTK_FOE_IB1_STATE;
+@@ -533,7 +534,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
+       if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW)
+               return;
+-      hlist_del_init(&entry->l2_data.list);
++      hlist_del_init(&entry->l2_list);
++      hlist_del_init(&entry->list);
+       kfree(entry);
+ }
+@@ -549,66 +551,55 @@ static int __mtk_foe_entry_idle_time(str
+               return now - timestamp;
+ }
++static bool
++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++{
++      struct mtk_foe_entry foe = {};
++      struct mtk_foe_entry *hwe;
++      u16 hash = entry->hash;
++      int len;
++
++      if (hash == 0xffff)
++              return false;
++
++      hwe = mtk_foe_get_entry(ppe, hash);
++      len = mtk_flow_entry_match_len(ppe->eth, &entry->data);
++      memcpy(&foe, hwe, len);
++
++      if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) ||
++          FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND)
++              return false;
++
++      entry->data.ib1 = foe.ib1;
++
++      return true;
++}
++
+ static void
+ mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+       u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
+       struct mtk_flow_entry *cur;
+-      struct mtk_foe_entry *hwe;
+       struct hlist_node *tmp;
+       int idle;
+       idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+-      hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) {
++      hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) {
+               int cur_idle;
+-              u32 ib1;
+-
+-              hwe = mtk_foe_get_entry(ppe, cur->hash);
+-              ib1 = READ_ONCE(hwe->ib1);
+-              if (FIELD_GET(MTK_FOE_IB1_STATE, ib1) != MTK_FOE_STATE_BIND) {
+-                      cur->hash = 0xffff;
+-                      __mtk_foe_entry_clear(ppe, cur);
++              if (!mtk_flow_entry_update(ppe, cur)) {
++                      __mtk_foe_entry_clear(ppe, entry, false);
+                       continue;
+               }
+-              cur_idle = __mtk_foe_entry_idle_time(ppe, ib1);
++              cur_idle = __mtk_foe_entry_idle_time(ppe, cur->data.ib1);
+               if (cur_idle >= idle)
+                       continue;
+               idle = cur_idle;
+               entry->data.ib1 &= ~ib1_ts_mask;
+-              entry->data.ib1 |= hwe->ib1 & ib1_ts_mask;
+-      }
+-}
+-
+-static void
+-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+-{
+-      struct mtk_foe_entry foe = {};
+-      struct mtk_foe_entry *hwe;
+-
+-      spin_lock_bh(&ppe_lock);
+-
+-      if (entry->type == MTK_FLOW_TYPE_L2) {
+-              mtk_flow_entry_update_l2(ppe, entry);
+-              goto out;
++              entry->data.ib1 |= cur->data.ib1 & ib1_ts_mask;
+       }
+-
+-      if (entry->hash == 0xffff)
+-              goto out;
+-
+-      hwe = mtk_foe_get_entry(ppe, entry->hash);
+-      memcpy(&foe, hwe, ppe->eth->soc->foe_entry_size);
+-      if (!mtk_flow_entry_match(ppe->eth, entry, &foe)) {
+-              entry->hash = 0xffff;
+-              goto out;
+-      }
+-
+-      entry->data.ib1 = foe.ib1;
+-
+-out:
+-      spin_unlock_bh(&ppe_lock);
+ }
+ static void
+@@ -651,7 +642,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+ void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+       spin_lock_bh(&ppe_lock);
+-      __mtk_foe_entry_clear(ppe, entry);
++      __mtk_foe_entry_clear(ppe, entry, true);
++      hlist_del_init(&entry->list);
+       spin_unlock_bh(&ppe_lock);
+ }
+@@ -698,8 +690,8 @@ mtk_foe_entry_commit_subflow(struct mtk_
+ {
+       const struct mtk_soc_data *soc = ppe->eth->soc;
+       struct mtk_flow_entry *flow_info;
+-      struct mtk_foe_entry foe = {}, *hwe;
+       struct mtk_foe_mac_info *l2;
++      struct mtk_foe_entry *hwe;
+       u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP;
+       int type;
+@@ -707,30 +699,30 @@ mtk_foe_entry_commit_subflow(struct mtk_
+       if (!flow_info)
+               return;
+-      flow_info->l2_data.base_flow = entry;
+       flow_info->type = MTK_FLOW_TYPE_L2_SUBFLOW;
+       flow_info->hash = hash;
+       hlist_add_head(&flow_info->list,
+                      &ppe->foe_flow[hash / soc->hash_offset]);
+-      hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows);
++      hlist_add_head(&flow_info->l2_list, &entry->l2_flows);
+       hwe = mtk_foe_get_entry(ppe, hash);
+-      memcpy(&foe, hwe, soc->foe_entry_size);
+-      foe.ib1 &= ib1_mask;
+-      foe.ib1 |= entry->data.ib1 & ~ib1_mask;
++      memcpy(&flow_info->data, hwe, soc->foe_entry_size);
++      flow_info->data.ib1 &= ib1_mask;
++      flow_info->data.ib1 |= entry->data.ib1 & ~ib1_mask;
+-      l2 = mtk_foe_entry_l2(ppe->eth, &foe);
++      l2 = mtk_foe_entry_l2(ppe->eth, &flow_info->data);
+       memcpy(l2, &entry->data.bridge.l2, sizeof(*l2));
+-      type = mtk_get_ib1_pkt_type(ppe->eth, foe.ib1);
++      type = mtk_get_ib1_pkt_type(ppe->eth, flow_info->data.ib1);
+       if (type == MTK_PPE_PKT_TYPE_IPV4_HNAPT)
+-              memcpy(&foe.ipv4.new, &foe.ipv4.orig, sizeof(foe.ipv4.new));
++              memcpy(&flow_info->data.ipv4.new, &flow_info->data.ipv4.orig,
++                     sizeof(flow_info->data.ipv4.new));
+       else if (type >= MTK_PPE_PKT_TYPE_IPV6_ROUTE_3T && l2->etype == ETH_P_IP)
+               l2->etype = ETH_P_IPV6;
+-      *mtk_foe_entry_ib2(ppe->eth, &foe) = entry->data.bridge.ib2;
++      *mtk_foe_entry_ib2(ppe->eth, &flow_info->data) = entry->data.bridge.ib2;
+-      __mtk_foe_entry_commit(ppe, &foe, hash);
++      __mtk_foe_entry_commit(ppe, &flow_info->data, hash);
+ }
+ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash)
+@@ -740,9 +732,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+       struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash);
+       struct mtk_flow_entry *entry;
+       struct mtk_foe_bridge key = {};
++      struct mtk_foe_entry foe = {};
+       struct hlist_node *n;
+       struct ethhdr *eh;
+       bool found = false;
++      int entry_len;
+       u8 *tag;
+       spin_lock_bh(&ppe_lock);
+@@ -750,20 +744,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe
+       if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND)
+               goto out;
+-      hlist_for_each_entry_safe(entry, n, head, list) {
+-              if (entry->type == MTK_FLOW_TYPE_L2_SUBFLOW) {
+-                      if (unlikely(FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) ==
+-                                   MTK_FOE_STATE_BIND))
+-                              continue;
+-
+-                      entry->hash = 0xffff;
+-                      __mtk_foe_entry_clear(ppe, entry);
+-                      continue;
+-              }
++      entry_len = mtk_flow_entry_match_len(ppe->eth, hwe);
++      memcpy(&foe, hwe, entry_len);
+-              if (found || !mtk_flow_entry_match(ppe->eth, entry, hwe)) {
++      hlist_for_each_entry_safe(entry, n, head, list) {
++              if (found ||
++                  !mtk_flow_entry_match(ppe->eth, entry, &foe, entry_len)) {
+                       if (entry->hash != 0xffff)
+-                              entry->hash = 0xffff;
++                              __mtk_foe_entry_clear(ppe, entry, false);
+                       continue;
+               }
+@@ -814,9 +802,17 @@ out:
+ int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+-      mtk_flow_entry_update(ppe, entry);
++      int idle;
++
++      spin_lock_bh(&ppe_lock);
++      if (entry->type == MTK_FLOW_TYPE_L2)
++              mtk_flow_entry_update_l2(ppe, entry);
++      else
++              mtk_flow_entry_update(ppe, entry);
++      idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
++      spin_unlock_bh(&ppe_lock);
+-      return __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
++      return idle;
+ }
+ int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -286,7 +286,12 @@ enum {
+ struct mtk_flow_entry {
+       union {
+-              struct hlist_node list;
++              /* regular flows + L2 subflows */
++              struct {
++                      struct hlist_node list;
++                      struct hlist_node l2_list;
++              };
++              /* L2 flows */
+               struct {
+                       struct rhash_head l2_node;
+                       struct hlist_head l2_flows;
+@@ -296,13 +301,7 @@ struct mtk_flow_entry {
+       s8 wed_index;
+       u8 ppe_index;
+       u16 hash;
+-      union {
+-              struct mtk_foe_entry data;
+-              struct {
+-                      struct mtk_flow_entry *base_flow;
+-                      struct hlist_node list;
+-              } l2_data;
+-      };
++      struct mtk_foe_entry data;
+       struct rhash_head node;
+       unsigned long cookie;
+ };
diff --git a/target/linux/generic/backport-6.6/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch b/target/linux/generic/backport-6.6/751-04-v6.4-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch
new file mode 100644 (file)
index 0000000..a93f80a
--- /dev/null
@@ -0,0 +1,343 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 23 Mar 2023 11:05:22 +0100
+Subject: [PATCH] net: ethernet: mediatek: fix ppe flow accounting for L2
+ flows
+
+For L2 flows, the packet/byte counters should report the sum of the
+counters of their subflows, both current and expired.
+In order to make this work, change the way that accounting data is tracked.
+Reset counters when a flow enters bind. Once it expires (or enters unbind),
+store the last counter value in struct mtk_flow_entry.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -80,9 +80,9 @@ static int mtk_ppe_mib_wait_busy(struct
+       int ret;
+       u32 val;
+-      ret = readl_poll_timeout(ppe->base + MTK_PPE_MIB_SER_CR, val,
+-                               !(val & MTK_PPE_MIB_SER_CR_ST),
+-                               20, MTK_PPE_WAIT_TIMEOUT_US);
++      ret = readl_poll_timeout_atomic(ppe->base + MTK_PPE_MIB_SER_CR, val,
++                                      !(val & MTK_PPE_MIB_SER_CR_ST),
++                                      20, MTK_PPE_WAIT_TIMEOUT_US);
+       if (ret)
+               dev_err(ppe->dev, "MIB table busy");
+@@ -90,17 +90,31 @@ static int mtk_ppe_mib_wait_busy(struct
+       return ret;
+ }
+-static int mtk_mib_entry_read(struct mtk_ppe *ppe, u16 index, u64 *bytes, u64 *packets)
++static inline struct mtk_foe_accounting *
++mtk_ppe_acct_data(struct mtk_ppe *ppe, u16 index)
++{
++      if (!ppe->acct_table)
++              return NULL;
++
++      return ppe->acct_table + index * sizeof(struct mtk_foe_accounting);
++}
++
++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index)
+ {
+       u32 val, cnt_r0, cnt_r1, cnt_r2;
++      struct mtk_foe_accounting *acct;
+       int ret;
+       val = FIELD_PREP(MTK_PPE_MIB_SER_CR_ADDR, index) | MTK_PPE_MIB_SER_CR_ST;
+       ppe_w32(ppe, MTK_PPE_MIB_SER_CR, val);
++      acct = mtk_ppe_acct_data(ppe, index);
++      if (!acct)
++              return NULL;
++
+       ret = mtk_ppe_mib_wait_busy(ppe);
+       if (ret)
+-              return ret;
++              return acct;
+       cnt_r0 = readl(ppe->base + MTK_PPE_MIB_SER_R0);
+       cnt_r1 = readl(ppe->base + MTK_PPE_MIB_SER_R1);
+@@ -109,19 +123,19 @@ static int mtk_mib_entry_read(struct mtk
+       if (mtk_is_netsys_v3_or_greater(ppe->eth)) {
+               /* 64 bit for each counter */
+               u32 cnt_r3 = readl(ppe->base + MTK_PPE_MIB_SER_R3);
+-              *bytes = ((u64)cnt_r1 << 32) | cnt_r0;
+-              *packets = ((u64)cnt_r3 << 32) | cnt_r2;
++              acct->bytes += ((u64)cnt_r1 << 32) | cnt_r0;
++              acct->packets += ((u64)cnt_r3 << 32) | cnt_r2;
+       } else {
+               /* 48 bit byte counter, 40 bit packet counter */
+               u32 byte_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R0_BYTE_CNT_LOW, cnt_r0);
+               u32 byte_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R1_BYTE_CNT_HIGH, cnt_r1);
+               u32 pkt_cnt_low = FIELD_GET(MTK_PPE_MIB_SER_R1_PKT_CNT_LOW, cnt_r1);
+               u32 pkt_cnt_high = FIELD_GET(MTK_PPE_MIB_SER_R2_PKT_CNT_HIGH, cnt_r2);
+-              *bytes = ((u64)byte_cnt_high << 32) | byte_cnt_low;
+-              *packets = ((u64)pkt_cnt_high << 16) | pkt_cnt_low;
++              acct->bytes += ((u64)byte_cnt_high << 32) | byte_cnt_low;
++              acct->packets += ((u64)pkt_cnt_high << 16) | pkt_cnt_low;
+       }
+-      return 0;
++      return acct;
+ }
+ static void mtk_ppe_cache_clear(struct mtk_ppe *ppe)
+@@ -520,14 +534,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp
+               hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID);
+               dma_wmb();
+               mtk_ppe_cache_clear(ppe);
+-
+-              if (ppe->accounting) {
+-                      struct mtk_foe_accounting *acct;
+-
+-                      acct = ppe->acct_table + entry->hash * sizeof(*acct);
+-                      acct->packets = 0;
+-                      acct->bytes = 0;
+-              }
+       }
+       entry->hash = 0xffff;
+@@ -552,11 +558,14 @@ static int __mtk_foe_entry_idle_time(str
+ }
+ static bool
+-mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
++mtk_flow_entry_update(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
++                    u64 *packets, u64 *bytes)
+ {
++      struct mtk_foe_accounting *acct;
+       struct mtk_foe_entry foe = {};
+       struct mtk_foe_entry *hwe;
+       u16 hash = entry->hash;
++      bool ret = false;
+       int len;
+       if (hash == 0xffff)
+@@ -567,18 +576,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp
+       memcpy(&foe, hwe, len);
+       if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) ||
+-          FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND)
+-              return false;
++          FIELD_GET(MTK_FOE_IB1_STATE, foe.ib1) != MTK_FOE_STATE_BIND) {
++              acct = mtk_ppe_acct_data(ppe, hash);
++              if (acct) {
++                      entry->prev_packets += acct->packets;
++                      entry->prev_bytes += acct->bytes;
++              }
++
++              goto out;
++      }
+       entry->data.ib1 = foe.ib1;
++      acct = mtk_ppe_mib_entry_read(ppe, hash);
++      ret = true;
++
++out:
++      if (acct) {
++              *packets += acct->packets;
++              *bytes += acct->bytes;
++      }
+-      return true;
++      return ret;
+ }
+ static void
+ mtk_flow_entry_update_l2(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+ {
+       u32 ib1_ts_mask = mtk_get_ib1_ts_mask(ppe->eth);
++      u64 *packets = &entry->packets;
++      u64 *bytes = &entry->bytes;
+       struct mtk_flow_entry *cur;
+       struct hlist_node *tmp;
+       int idle;
+@@ -587,7 +613,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe
+       hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) {
+               int cur_idle;
+-              if (!mtk_flow_entry_update(ppe, cur)) {
++              if (!mtk_flow_entry_update(ppe, cur, packets, bytes)) {
++                      entry->prev_packets += cur->prev_packets;
++                      entry->prev_bytes += cur->prev_bytes;
+                       __mtk_foe_entry_clear(ppe, entry, false);
+                       continue;
+               }
+@@ -602,10 +630,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe
+       }
+ }
++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
++                           int *idle)
++{
++      entry->packets = entry->prev_packets;
++      entry->bytes = entry->prev_bytes;
++
++      spin_lock_bh(&ppe_lock);
++
++      if (entry->type == MTK_FLOW_TYPE_L2)
++              mtk_flow_entry_update_l2(ppe, entry);
++      else
++              mtk_flow_entry_update(ppe, entry, &entry->packets, &entry->bytes);
++
++      *idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
++
++      spin_unlock_bh(&ppe_lock);
++}
++
+ static void
+ __mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_foe_entry *entry,
+                      u16 hash)
+ {
++      struct mtk_foe_accounting *acct;
+       struct mtk_eth *eth = ppe->eth;
+       u16 timestamp = mtk_eth_timestamp(eth);
+       struct mtk_foe_entry *hwe;
+@@ -636,6 +683,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p
+       dma_wmb();
++      acct = mtk_ppe_mib_entry_read(ppe, hash);
++      if (acct) {
++              acct->packets = 0;
++              acct->bytes = 0;
++      }
++
+       mtk_ppe_cache_clear(ppe);
+ }
+@@ -800,21 +853,6 @@ out:
+       spin_unlock_bh(&ppe_lock);
+ }
+-int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry)
+-{
+-      int idle;
+-
+-      spin_lock_bh(&ppe_lock);
+-      if (entry->type == MTK_FLOW_TYPE_L2)
+-              mtk_flow_entry_update_l2(ppe, entry);
+-      else
+-              mtk_flow_entry_update(ppe, entry);
+-      idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1);
+-      spin_unlock_bh(&ppe_lock);
+-
+-      return idle;
+-}
+-
+ int mtk_ppe_prepare_reset(struct mtk_ppe *ppe)
+ {
+       if (!ppe)
+@@ -842,32 +880,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe
+       return mtk_ppe_wait_busy(ppe);
+ }
+-struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
+-                                               struct mtk_foe_accounting *diff)
+-{
+-      struct mtk_foe_accounting *acct;
+-      int size = sizeof(struct mtk_foe_accounting);
+-      u64 bytes, packets;
+-
+-      if (!ppe->accounting)
+-              return NULL;
+-
+-      if (mtk_mib_entry_read(ppe, index, &bytes, &packets))
+-              return NULL;
+-
+-      acct = ppe->acct_table + index * size;
+-
+-      acct->bytes += bytes;
+-      acct->packets += packets;
+-
+-      if (diff) {
+-              diff->bytes = bytes;
+-              diff->packets = packets;
+-      }
+-
+-      return acct;
+-}
+-
+ struct mtk_ppe *mtk_ppe_init(struct mtk_eth *eth, void __iomem *base, int index)
+ {
+       bool accounting = eth->soc->has_accounting;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -304,6 +304,8 @@ struct mtk_flow_entry {
+       struct mtk_foe_entry data;
+       struct rhash_head node;
+       unsigned long cookie;
++      u64 prev_packets, prev_bytes;
++      u64 packets, bytes;
+ };
+ struct mtk_mib_entry {
+@@ -348,6 +350,7 @@ void mtk_ppe_deinit(struct mtk_eth *eth)
+ void mtk_ppe_start(struct mtk_ppe *ppe);
+ int mtk_ppe_stop(struct mtk_ppe *ppe);
+ int mtk_ppe_prepare_reset(struct mtk_ppe *ppe);
++struct mtk_foe_accounting *mtk_ppe_mib_entry_read(struct mtk_ppe *ppe, u16 index);
+ void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash);
+@@ -396,9 +399,8 @@ int mtk_foe_entry_set_queue(struct mtk_e
+                           unsigned int queue);
+ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+ void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+-int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+ int mtk_ppe_debugfs_init(struct mtk_ppe *ppe, int index);
+-struct mtk_foe_accounting *mtk_foe_entry_get_mib(struct mtk_ppe *ppe, u32 index,
+-                                               struct mtk_foe_accounting *diff);
++void mtk_foe_entry_get_stats(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
++                           int *idle);
+ #endif
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_debugfs.c
+@@ -96,7 +96,7 @@ mtk_ppe_debugfs_foe_show(struct seq_file
+               if (bind && state != MTK_FOE_STATE_BIND)
+                       continue;
+-              acct = mtk_foe_entry_get_mib(ppe, i, NULL);
++              acct = mtk_ppe_mib_entry_read(ppe, i);
+               type = mtk_get_ib1_pkt_type(ppe->eth, entry->ib1);
+               seq_printf(m, "%05x %s %7s", i,
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -501,24 +501,21 @@ static int
+ mtk_flow_offload_stats(struct mtk_eth *eth, struct flow_cls_offload *f)
+ {
+       struct mtk_flow_entry *entry;
+-      struct mtk_foe_accounting diff;
+-      u32 idle;
++      u64 packets, bytes;
++      int idle;
+       entry = rhashtable_lookup(&eth->flow_table, &f->cookie,
+                                 mtk_flow_ht_params);
+       if (!entry)
+               return -ENOENT;
+-      idle = mtk_foe_entry_idle_time(eth->ppe[entry->ppe_index], entry);
++      packets = entry->packets;
++      bytes = entry->bytes;
++      mtk_foe_entry_get_stats(eth->ppe[entry->ppe_index], entry, &idle);
++      f->stats.pkts += entry->packets - packets;
++      f->stats.bytes += entry->bytes - bytes;
+       f->stats.lastused = jiffies - idle * HZ;
+-      if (entry->hash != 0xFFFF &&
+-          mtk_foe_entry_get_mib(eth->ppe[entry->ppe_index], entry->hash,
+-                                &diff)) {
+-              f->stats.pkts += diff.packets;
+-              f->stats.bytes += diff.bytes;
+-      }
+-
+       return 0;
+ }
diff --git a/target/linux/generic/backport-6.6/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch b/target/linux/generic/backport-6.6/752-03-v6.6-net-ethernet-mtk_eth_soc-rely-on-mtk_pse_port-defini.patch
new file mode 100644 (file)
index 0000000..0bf9dea
--- /dev/null
@@ -0,0 +1,29 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Tue, 12 Sep 2023 10:22:56 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: rely on mtk_pse_port definitions
+ in mtk_flow_set_output_device
+
+Similar to ethernet ports, rely on mtk_pse_port definitions for
+pse wdma ports as well.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://lore.kernel.org/r/b86bdb717e963e3246c1dec5f736c810703cf056.1694506814.git.lorenzo@kernel.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -196,10 +196,10 @@ mtk_flow_set_output_device(struct mtk_et
+               if (mtk_is_netsys_v2_or_greater(eth)) {
+                       switch (info.wdma_idx) {
+                       case 0:
+-                              pse_port = 8;
++                              pse_port = PSE_WDMA0_PORT;
+                               break;
+                       case 1:
+-                              pse_port = 9;
++                              pse_port = PSE_WDMA1_PORT;
+                               break;
+                       default:
+                               return -EINVAL;
diff --git a/target/linux/generic/backport-6.6/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch b/target/linux/generic/backport-6.6/752-05-v6.7-net-ethernet-mtk_wed-do-not-assume-offload-callbacks.patch
new file mode 100644 (file)
index 0000000..b9d3582
--- /dev/null
@@ -0,0 +1,68 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Wed, 13 Sep 2023 20:42:47 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: do not assume offload callbacks are
+ always set
+
+Check if wlan.offload_enable and wlan.offload_disable callbacks are set
+in mtk_wed_flow_add/mtk_wed_flow_remove since mt7996 will not rely
+on them.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -1709,19 +1709,20 @@ mtk_wed_irq_set_mask(struct mtk_wed_devi
+ int mtk_wed_flow_add(int index)
+ {
+       struct mtk_wed_hw *hw = hw_list[index];
+-      int ret;
++      int ret = 0;
+-      if (!hw || !hw->wed_dev)
+-              return -ENODEV;
++      mutex_lock(&hw_lock);
+-      if (hw->num_flows) {
+-              hw->num_flows++;
+-              return 0;
++      if (!hw || !hw->wed_dev) {
++              ret = -ENODEV;
++              goto out;
+       }
+-      mutex_lock(&hw_lock);
+-      if (!hw->wed_dev) {
+-              ret = -ENODEV;
++      if (!hw->wed_dev->wlan.offload_enable)
++              goto out;
++
++      if (hw->num_flows) {
++              hw->num_flows++;
+               goto out;
+       }
+@@ -1740,14 +1741,15 @@ void mtk_wed_flow_remove(int index)
+ {
+       struct mtk_wed_hw *hw = hw_list[index];
+-      if (!hw)
+-              return;
++      mutex_lock(&hw_lock);
+-      if (--hw->num_flows)
+-              return;
++      if (!hw || !hw->wed_dev)
++              goto out;
+-      mutex_lock(&hw_lock);
+-      if (!hw->wed_dev)
++      if (!hw->wed_dev->wlan.offload_disable)
++              goto out;
++
++      if (--hw->num_flows)
+               goto out;
+       hw->wed_dev->wlan.offload_disable(hw->wed_dev);
diff --git a/target/linux/generic/backport-6.6/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch b/target/linux/generic/backport-6.6/752-06-v6.7-net-ethernet-mtk_wed-introduce-versioning-utility-ro.patch
new file mode 100644 (file)
index 0000000..323bc14
--- /dev/null
@@ -0,0 +1,232 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Sep 2023 12:29:05 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: introduce versioning utility routines
+
+Similar to mtk_eth_soc, introduce the following wed versioning
+utility routines:
+- mtk_wed_is_v1
+- mtk_wed_is_v2
+
+This is a preliminary patch to introduce WED support for MT7988 SoC
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -278,7 +278,7 @@ mtk_wed_assign(struct mtk_wed_device *de
+               if (!hw->wed_dev)
+                       goto out;
+-              if (hw->version == 1)
++              if (mtk_wed_is_v1(hw))
+                       return NULL;
+               /* MT7986 WED devices do not have any pcie slot restrictions */
+@@ -359,7 +359,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
+                       desc->buf0 = cpu_to_le32(buf_phys);
+                       desc->buf1 = cpu_to_le32(buf_phys + txd_size);
+-                      if (dev->hw->version == 1)
++                      if (mtk_wed_is_v1(dev->hw))
+                               ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
+                                      FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
+                                                 MTK_WED_BUF_SIZE - txd_size) |
+@@ -498,7 +498,7 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
+ {
+       u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
+-      if (dev->hw->version == 1)
++      if (mtk_wed_is_v1(dev->hw))
+               mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
+       else
+               mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
+@@ -577,7 +577,7 @@ mtk_wed_dma_disable(struct mtk_wed_devic
+                MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
+                MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
+-      if (dev->hw->version == 1) {
++      if (mtk_wed_is_v1(dev->hw)) {
+               regmap_write(dev->hw->mirror, dev->hw->index * 4, 0);
+               wdma_clr(dev, MTK_WDMA_GLO_CFG,
+                        MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
+@@ -606,7 +606,7 @@ mtk_wed_stop(struct mtk_wed_device *dev)
+       wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
+       wdma_w32(dev, MTK_WDMA_INT_GRP2, 0);
+-      if (dev->hw->version == 1)
++      if (mtk_wed_is_v1(dev->hw))
+               return;
+       wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
+@@ -624,7 +624,7 @@ mtk_wed_deinit(struct mtk_wed_device *de
+               MTK_WED_CTRL_WED_TX_BM_EN |
+               MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+-      if (dev->hw->version == 1)
++      if (mtk_wed_is_v1(dev->hw))
+               return;
+       wed_clr(dev, MTK_WED_CTRL,
+@@ -730,7 +730,7 @@ mtk_wed_bus_init(struct mtk_wed_device *
+ static void
+ mtk_wed_set_wpdma(struct mtk_wed_device *dev)
+ {
+-      if (dev->hw->version == 1) {
++      if (mtk_wed_is_v1(dev->hw)) {
+               wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_phys);
+       } else {
+               mtk_wed_bus_init(dev);
+@@ -761,7 +761,7 @@ mtk_wed_hw_init_early(struct mtk_wed_dev
+             MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
+       wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set);
+-      if (dev->hw->version == 1) {
++      if (mtk_wed_is_v1(dev->hw)) {
+               u32 offset = dev->hw->index ? 0x04000400 : 0;
+               wdma_set(dev, MTK_WDMA_GLO_CFG,
+@@ -934,7 +934,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d
+       wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
+-      if (dev->hw->version == 1) {
++      if (mtk_wed_is_v1(dev->hw)) {
+               wed_w32(dev, MTK_WED_TX_BM_TKID,
+                       FIELD_PREP(MTK_WED_TX_BM_TKID_START,
+                                  dev->wlan.token_start) |
+@@ -967,7 +967,7 @@ mtk_wed_hw_init(struct mtk_wed_device *d
+       mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
+-      if (dev->hw->version == 1) {
++      if (mtk_wed_is_v1(dev->hw)) {
+               wed_set(dev, MTK_WED_CTRL,
+                       MTK_WED_CTRL_WED_TX_BM_EN |
+                       MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+@@ -1217,7 +1217,7 @@ mtk_wed_reset_dma(struct mtk_wed_device
+       }
+       dev->init_done = false;
+-      if (dev->hw->version == 1)
++      if (mtk_wed_is_v1(dev->hw))
+               return;
+       if (!busy) {
+@@ -1343,7 +1343,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev
+               MTK_WED_CTRL_WED_TX_BM_EN |
+               MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+-      if (dev->hw->version == 1) {
++      if (mtk_wed_is_v1(dev->hw)) {
+               wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER,
+                       MTK_WED_PCIE_INT_TRIGGER_STATUS);
+@@ -1416,7 +1416,7 @@ mtk_wed_dma_enable(struct mtk_wed_device
+                MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
+                MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
+-      if (dev->hw->version == 1) {
++      if (mtk_wed_is_v1(dev->hw)) {
+               wdma_set(dev, MTK_WDMA_GLO_CFG,
+                        MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
+       } else {
+@@ -1465,7 +1465,7 @@ mtk_wed_start(struct mtk_wed_device *dev
+       mtk_wed_set_ext_int(dev, true);
+-      if (dev->hw->version == 1) {
++      if (mtk_wed_is_v1(dev->hw)) {
+               u32 val = dev->wlan.wpdma_phys | MTK_PCIE_MIRROR_MAP_EN |
+                         FIELD_PREP(MTK_PCIE_MIRROR_MAP_WED_ID,
+                                    dev->hw->index);
+@@ -1550,7 +1550,7 @@ mtk_wed_attach(struct mtk_wed_device *de
+       }
+       mtk_wed_hw_init_early(dev);
+-      if (hw->version == 1) {
++      if (mtk_wed_is_v1(hw)) {
+               regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
+                                  BIT(hw->index), 0);
+       } else {
+@@ -1618,7 +1618,7 @@ static int
+ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
+ {
+       struct mtk_wed_ring *ring = &dev->txfree_ring;
+-      int i, index = dev->hw->version == 1;
++      int i, index = mtk_wed_is_v1(dev->hw);
+       /*
+        * For txfree event handling, the same DMA ring is shared between WED
+@@ -1676,7 +1676,7 @@ mtk_wed_irq_get(struct mtk_wed_device *d
+ {
+       u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
+-      if (dev->hw->version == 1)
++      if (mtk_wed_is_v1(dev->hw))
+               ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
+       else
+               ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
+@@ -1842,7 +1842,7 @@ mtk_wed_setup_tc(struct mtk_wed_device *
+ {
+       struct mtk_wed_hw *hw = wed->hw;
+-      if (hw->version < 2)
++      if (mtk_wed_is_v1(hw))
+               return -EOPNOTSUPP;
+       switch (type) {
+@@ -1916,9 +1916,9 @@ void mtk_wed_add_hw(struct device_node *
+       hw->wdma = wdma;
+       hw->index = index;
+       hw->irq = irq;
+-      hw->version = mtk_is_netsys_v1(eth) ? 1 : 2;
++      hw->version = eth->soc->version;
+-      if (hw->version == 1) {
++      if (mtk_wed_is_v1(hw)) {
+               hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
+                               "mediatek,pcie-mirror");
+               hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np,
+--- a/drivers/net/ethernet/mediatek/mtk_wed.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
+@@ -40,6 +40,16 @@ struct mtk_wdma_info {
+ };
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
++static inline bool mtk_wed_is_v1(struct mtk_wed_hw *hw)
++{
++      return hw->version == 1;
++}
++
++static inline bool mtk_wed_is_v2(struct mtk_wed_hw *hw)
++{
++      return hw->version == 2;
++}
++
+ static inline void
+ wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val)
+ {
+--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
+@@ -261,7 +261,7 @@ void mtk_wed_hw_add_debugfs(struct mtk_w
+       debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg);
+       debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval);
+       debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops);
+-      if (hw->version != 1)
++      if (!mtk_wed_is_v1(hw))
+               debugfs_create_file_unsafe("rxinfo", 0400, dir, hw,
+                                          &wed_rxinfo_fops);
+ }
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we
+ {
+       struct mtk_wed_wo *wo = dev->hw->wed_wo;
+-      if (dev->hw->version == 1)
++      if (mtk_wed_is_v1(dev->hw))
+               return 0;
+       if (WARN_ON(!wo))
diff --git a/target/linux/generic/backport-6.6/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch b/target/linux/generic/backport-6.6/752-07-v6.7-net-ethernet-mtk_wed-do-not-configure-rx-offload-if-.patch
new file mode 100644 (file)
index 0000000..02ef4e6
--- /dev/null
@@ -0,0 +1,234 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Sep 2023 12:29:06 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: do not configure rx offload if not
+ supported
+
+Check if rx offload is supported running mtk_wed_get_rx_capa routine
+before configuring it. This is a preliminary patch to introduce Wireless
+Ethernet Dispatcher (WED) support for MT7988 SoC.
+
+Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -606,7 +606,7 @@ mtk_wed_stop(struct mtk_wed_device *dev)
+       wdma_w32(dev, MTK_WDMA_INT_MASK, 0);
+       wdma_w32(dev, MTK_WDMA_INT_GRP2, 0);
+-      if (mtk_wed_is_v1(dev->hw))
++      if (!mtk_wed_get_rx_capa(dev))
+               return;
+       wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
+@@ -732,16 +732,21 @@ mtk_wed_set_wpdma(struct mtk_wed_device
+ {
+       if (mtk_wed_is_v1(dev->hw)) {
+               wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_phys);
+-      } else {
+-              mtk_wed_bus_init(dev);
+-
+-              wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int);
+-              wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask);
+-              wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
+-              wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree);
+-              wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
+-              wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
++              return;
+       }
++
++      mtk_wed_bus_init(dev);
++
++      wed_w32(dev, MTK_WED_WPDMA_CFG_BASE, dev->wlan.wpdma_int);
++      wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK, dev->wlan.wpdma_mask);
++      wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
++      wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE, dev->wlan.wpdma_txfree);
++
++      if (!mtk_wed_get_rx_capa(dev))
++              return;
++
++      wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
++      wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
+ }
+ static void
+@@ -973,15 +978,17 @@ mtk_wed_hw_init(struct mtk_wed_device *d
+                       MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+       } else {
+               wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
+-              /* rx hw init */
+-              wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
+-                      MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
+-                      MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
+-              wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
+-
+-              mtk_wed_rx_buffer_hw_init(dev);
+-              mtk_wed_rro_hw_init(dev);
+-              mtk_wed_route_qm_hw_init(dev);
++              if (mtk_wed_get_rx_capa(dev)) {
++                      /* rx hw init */
++                      wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
++                              MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
++                              MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
++                      wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
++
++                      mtk_wed_rx_buffer_hw_init(dev);
++                      mtk_wed_rro_hw_init(dev);
++                      mtk_wed_route_qm_hw_init(dev);
++              }
+       }
+       wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE);
+@@ -1353,8 +1360,6 @@ mtk_wed_configure_irq(struct mtk_wed_dev
+               wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
+       } else {
+-              wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE,
+-                                      GENMASK(1, 0));
+               /* initail tx interrupt trigger */
+               wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
+                       MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
+@@ -1373,15 +1378,20 @@ mtk_wed_configure_irq(struct mtk_wed_dev
+                       FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG,
+                                  dev->wlan.txfree_tbit));
+-              wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
+-                      MTK_WED_WPDMA_INT_CTRL_RX0_EN |
+-                      MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
+-                      MTK_WED_WPDMA_INT_CTRL_RX1_EN |
+-                      MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
+-                      FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
+-                                 dev->wlan.rx_tbit[0]) |
+-                      FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
+-                                 dev->wlan.rx_tbit[1]));
++              if (mtk_wed_get_rx_capa(dev)) {
++                      wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
++                              MTK_WED_WPDMA_INT_CTRL_RX0_EN |
++                              MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
++                              MTK_WED_WPDMA_INT_CTRL_RX1_EN |
++                              MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
++                              FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
++                                         dev->wlan.rx_tbit[0]) |
++                              FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
++                                         dev->wlan.rx_tbit[1]));
++
++                      wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE,
++                                              GENMASK(1, 0));
++              }
+               wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask);
+               wed_set(dev, MTK_WED_WDMA_INT_CTRL,
+@@ -1400,6 +1410,8 @@ mtk_wed_configure_irq(struct mtk_wed_dev
+ static void
+ mtk_wed_dma_enable(struct mtk_wed_device *dev)
+ {
++      int i;
++
+       wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
+       wed_set(dev, MTK_WED_GLO_CFG,
+@@ -1419,33 +1431,33 @@ mtk_wed_dma_enable(struct mtk_wed_device
+       if (mtk_wed_is_v1(dev->hw)) {
+               wdma_set(dev, MTK_WDMA_GLO_CFG,
+                        MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
+-      } else {
+-              int i;
++              return;
++      }
+-              wed_set(dev, MTK_WED_WPDMA_CTRL,
+-                      MTK_WED_WPDMA_CTRL_SDL1_FIXED);
++      wed_set(dev, MTK_WED_WPDMA_CTRL,
++              MTK_WED_WPDMA_CTRL_SDL1_FIXED);
++      wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
++              MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
++              MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
++      wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
++              MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
++              MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
+-              wed_set(dev, MTK_WED_WDMA_GLO_CFG,
+-                      MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
+-                      MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
++      if (!mtk_wed_get_rx_capa(dev))
++              return;
+-              wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
+-                      MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
+-                      MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
+-
+-              wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
+-                      MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
+-                      MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
++      wed_set(dev, MTK_WED_WDMA_GLO_CFG,
++              MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
++              MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
+-              wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
+-                      MTK_WED_WPDMA_RX_D_RX_DRV_EN |
+-                      FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
+-                      FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
+-                                 0x2));
++      wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
++              MTK_WED_WPDMA_RX_D_RX_DRV_EN |
++              FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
++              FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
++                         0x2));
+-              for (i = 0; i < MTK_WED_RX_QUEUES; i++)
+-                      mtk_wed_check_wfdma_rx_fill(dev, i);
+-      }
++      for (i = 0; i < MTK_WED_RX_QUEUES; i++)
++              mtk_wed_check_wfdma_rx_fill(dev, i);
+ }
+ static void
+@@ -1472,7 +1484,7 @@ mtk_wed_start(struct mtk_wed_device *dev
+               val |= BIT(0) | (BIT(1) * !!dev->hw->index);
+               regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
+-      } else {
++      } else if (mtk_wed_get_rx_capa(dev)) {
+               /* driver set mid ready and only once */
+               wed_w32(dev, MTK_WED_EXT_INT_MASK1,
+                       MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
+@@ -1484,7 +1496,6 @@ mtk_wed_start(struct mtk_wed_device *dev
+               if (mtk_wed_rro_cfg(dev))
+                       return;
+-
+       }
+       mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
+@@ -1550,13 +1561,14 @@ mtk_wed_attach(struct mtk_wed_device *de
+       }
+       mtk_wed_hw_init_early(dev);
+-      if (mtk_wed_is_v1(hw)) {
++      if (mtk_wed_is_v1(hw))
+               regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
+                                  BIT(hw->index), 0);
+-      } else {
++      else
+               dev->rev_id = wed_r32(dev, MTK_WED_REV_ID);
++
++      if (mtk_wed_get_rx_capa(dev))
+               ret = mtk_wed_wo_init(hw);
+-      }
+ out:
+       if (ret) {
+               dev_err(dev->hw->dev, "failed to attach wed device\n");
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -207,7 +207,7 @@ int mtk_wed_mcu_msg_update(struct mtk_we
+ {
+       struct mtk_wed_wo *wo = dev->hw->wed_wo;
+-      if (mtk_wed_is_v1(dev->hw))
++      if (!mtk_wed_get_rx_capa(dev))
+               return 0;
+       if (WARN_ON(!wo))
diff --git a/target/linux/generic/backport-6.6/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch b/target/linux/generic/backport-6.6/752-08-v6.7-net-ethernet-mtk_wed-rename-mtk_rxbm_desc-in-mtk_wed.patch
new file mode 100644 (file)
index 0000000..618624a
--- /dev/null
@@ -0,0 +1,52 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Sep 2023 12:29:07 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: rename mtk_rxbm_desc in
+ mtk_wed_bm_desc
+
+Rename mtk_rxbm_desc structure in mtk_wed_bm_desc since it will be used
+even on tx side by MT7988 SoC.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -422,7 +422,7 @@ free_pagelist:
+ static int
+ mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev)
+ {
+-      struct mtk_rxbm_desc *desc;
++      struct mtk_wed_bm_desc *desc;
+       dma_addr_t desc_phys;
+       dev->rx_buf_ring.size = dev->wlan.rx_nbuf;
+@@ -442,7 +442,7 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d
+ static void
+ mtk_wed_free_rx_buffer(struct mtk_wed_device *dev)
+ {
+-      struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc;
++      struct mtk_wed_bm_desc *desc = dev->rx_buf_ring.desc;
+       if (!desc)
+               return;
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -45,7 +45,7 @@ enum mtk_wed_wo_cmd {
+       MTK_WED_WO_CMD_WED_END
+ };
+-struct mtk_rxbm_desc {
++struct mtk_wed_bm_desc {
+       __le32 buf0;
+       __le32 token;
+ } __packed __aligned(4);
+@@ -104,7 +104,7 @@ struct mtk_wed_device {
+       struct {
+               int size;
+-              struct mtk_rxbm_desc *desc;
++              struct mtk_wed_bm_desc *desc;
+               dma_addr_t desc_phys;
+       } rx_buf_ring;
diff --git a/target/linux/generic/backport-6.6/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch b/target/linux/generic/backport-6.6/752-09-v6.7-net-ethernet-mtk_wed-introduce-mtk_wed_buf-structure.patch
new file mode 100644 (file)
index 0000000..15dbaf0
--- /dev/null
@@ -0,0 +1,87 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Sep 2023 12:29:08 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: introduce mtk_wed_buf structure
+
+Introduce mtk_wed_buf structure to store both virtual and physical
+addresses allocated in mtk_wed_tx_buffer_alloc() routine. This is a
+preliminary patch to add WED support for MT7988 SoC since it relies on a
+different dma descriptor layout not storing page dma addresses.
+
+Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -300,9 +300,9 @@ out:
+ static int
+ mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
+ {
++      struct mtk_wed_buf *page_list;
+       struct mtk_wdma_desc *desc;
+       dma_addr_t desc_phys;
+-      void **page_list;
+       int token = dev->wlan.token_start;
+       int ring_size;
+       int n_pages;
+@@ -343,7 +343,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
+                       return -ENOMEM;
+               }
+-              page_list[page_idx++] = page;
++              page_list[page_idx].p = page;
++              page_list[page_idx++].phy_addr = page_phys;
+               dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE,
+                                       DMA_BIDIRECTIONAL);
+@@ -387,8 +388,8 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
+ static void
+ mtk_wed_free_tx_buffer(struct mtk_wed_device *dev)
+ {
++      struct mtk_wed_buf *page_list = dev->tx_buf_ring.pages;
+       struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc;
+-      void **page_list = dev->tx_buf_ring.pages;
+       int page_idx;
+       int i;
+@@ -400,13 +401,12 @@ mtk_wed_free_tx_buffer(struct mtk_wed_de
+       for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size;
+            i += MTK_WED_BUF_PER_PAGE) {
+-              void *page = page_list[page_idx++];
+-              dma_addr_t buf_addr;
++              dma_addr_t buf_addr = page_list[page_idx].phy_addr;
++              void *page = page_list[page_idx++].p;
+               if (!page)
+                       break;
+-              buf_addr = le32_to_cpu(desc[i].buf0);
+               dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE,
+                              DMA_BIDIRECTIONAL);
+               __free_page(page);
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -76,6 +76,11 @@ struct mtk_wed_wo_rx_stats {
+       __le32 rx_drop_cnt;
+ };
++struct mtk_wed_buf {
++      void *p;
++      dma_addr_t phy_addr;
++};
++
+ struct mtk_wed_device {
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+       const struct mtk_wed_ops *ops;
+@@ -97,7 +102,7 @@ struct mtk_wed_device {
+       struct {
+               int size;
+-              void **pages;
++              struct mtk_wed_buf *pages;
+               struct mtk_wdma_desc *desc;
+               dma_addr_t desc_phys;
+       } tx_buf_ring;
diff --git a/target/linux/generic/backport-6.6/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch b/target/linux/generic/backport-6.6/752-10-v6.7-net-ethernet-mtk_wed-move-mem_region-array-out-of-mt.patch
new file mode 100644 (file)
index 0000000..98d782b
--- /dev/null
@@ -0,0 +1,88 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Sep 2023 12:29:09 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: move mem_region array out of
+ mtk_wed_mcu_load_firmware
+
+Remove mtk_wed_wo_memory_region boot structure in mtk_wed_wo.
+This is a preliminary patch to introduce WED support for MT7988 SoC.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -16,14 +16,30 @@
+ #include "mtk_wed_wo.h"
+ #include "mtk_wed.h"
++static struct mtk_wed_wo_memory_region mem_region[] = {
++      [MTK_WED_WO_REGION_EMI] = {
++              .name = "wo-emi",
++      },
++      [MTK_WED_WO_REGION_ILM] = {
++              .name = "wo-ilm",
++      },
++      [MTK_WED_WO_REGION_DATA] = {
++              .name = "wo-data",
++              .shared = true,
++      },
++      [MTK_WED_WO_REGION_BOOT] = {
++              .name = "wo-boot",
++      },
++};
++
+ static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg)
+ {
+-      return readl(wo->boot.addr + reg);
++      return readl(mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
+ }
+ static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
+ {
+-      writel(val, wo->boot.addr + reg);
++      writel(val, mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
+ }
+ static struct sk_buff *
+@@ -294,18 +310,6 @@ next:
+ static int
+ mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo)
+ {
+-      static struct mtk_wed_wo_memory_region mem_region[] = {
+-              [MTK_WED_WO_REGION_EMI] = {
+-                      .name = "wo-emi",
+-              },
+-              [MTK_WED_WO_REGION_ILM] = {
+-                      .name = "wo-ilm",
+-              },
+-              [MTK_WED_WO_REGION_DATA] = {
+-                      .name = "wo-data",
+-                      .shared = true,
+-              },
+-      };
+       const struct mtk_wed_fw_trailer *trailer;
+       const struct firmware *fw;
+       const char *fw_name;
+@@ -319,11 +323,6 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
+                       return ret;
+       }
+-      wo->boot.name = "wo-boot";
+-      ret = mtk_wed_get_memory_region(wo, &wo->boot);
+-      if (ret)
+-              return ret;
+-
+       /* set dummy cr */
+       wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL,
+               wo->hw->index + 1);
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
+@@ -228,7 +228,6 @@ struct mtk_wed_wo_queue {
+ struct mtk_wed_wo {
+       struct mtk_wed_hw *hw;
+-      struct mtk_wed_wo_memory_region boot;
+       struct mtk_wed_wo_queue q_tx;
+       struct mtk_wed_wo_queue q_rx;
diff --git a/target/linux/generic/backport-6.6/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch b/target/linux/generic/backport-6.6/752-11-v6.7-net-ethernet-mtk_wed-make-memory-region-optional.patch
new file mode 100644 (file)
index 0000000..48b0d02
--- /dev/null
@@ -0,0 +1,71 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Sep 2023 12:29:10 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: make memory region optional
+
+Make mtk_wed_wo_memory_region optionals.
+This is a preliminary patch to introduce Wireless Ethernet Dispatcher
+support for MT7988 SoC since MT7988 WED fw image will have a different
+layout.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -234,19 +234,13 @@ int mtk_wed_mcu_msg_update(struct mtk_we
+ }
+ static int
+-mtk_wed_get_memory_region(struct mtk_wed_wo *wo,
++mtk_wed_get_memory_region(struct mtk_wed_hw *hw, int index,
+                         struct mtk_wed_wo_memory_region *region)
+ {
+       struct reserved_mem *rmem;
+       struct device_node *np;
+-      int index;
+-      index = of_property_match_string(wo->hw->node, "memory-region-names",
+-                                       region->name);
+-      if (index < 0)
+-              return index;
+-
+-      np = of_parse_phandle(wo->hw->node, "memory-region", index);
++      np = of_parse_phandle(hw->node, "memory-region", index);
+       if (!np)
+               return -ENODEV;
+@@ -258,7 +252,7 @@ mtk_wed_get_memory_region(struct mtk_wed
+       region->phy_addr = rmem->base;
+       region->size = rmem->size;
+-      region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size);
++      region->addr = devm_ioremap(hw->dev, region->phy_addr, region->size);
+       return !region->addr ? -EINVAL : 0;
+ }
+@@ -271,6 +265,9 @@ mtk_wed_mcu_run_firmware(struct mtk_wed_
+       const struct mtk_wed_fw_trailer *trailer;
+       const struct mtk_wed_fw_region *fw_region;
++      if (!region->phy_addr || !region->size)
++              return 0;
++
+       trailer_ptr = fw->data + fw->size - sizeof(*trailer);
+       trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr;
+       region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region);
+@@ -318,7 +315,13 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
+       /* load firmware region metadata */
+       for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
+-              ret = mtk_wed_get_memory_region(wo, &mem_region[i]);
++              int index = of_property_match_string(wo->hw->node,
++                                                   "memory-region-names",
++                                                   mem_region[i].name);
++              if (index < 0)
++                      continue;
++
++              ret = mtk_wed_get_memory_region(wo->hw, index, &mem_region[i]);
+               if (ret)
+                       return ret;
+       }
diff --git a/target/linux/generic/backport-6.6/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch b/target/linux/generic/backport-6.6/752-13-v6.7-net-ethernet-mtk_wed-add-mtk_wed_soc_data-structure.patch
new file mode 100644 (file)
index 0000000..6e2f8b1
--- /dev/null
@@ -0,0 +1,217 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Sep 2023 12:29:12 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: add mtk_wed_soc_data structure
+
+Introduce mtk_wed_soc_data utility structure to contain per-SoC
+definitions.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -49,6 +49,26 @@ struct mtk_wed_flow_block_priv {
+       struct net_device *dev;
+ };
++static const struct mtk_wed_soc_data mt7622_data = {
++      .regmap = {
++              .tx_bm_tkid             = 0x088,
++              .wpdma_rx_ring0         = 0x770,
++              .reset_idx_tx_mask      = GENMASK(3, 0),
++              .reset_idx_rx_mask      = GENMASK(17, 16),
++      },
++      .wdma_desc_size = sizeof(struct mtk_wdma_desc),
++};
++
++static const struct mtk_wed_soc_data mt7986_data = {
++      .regmap = {
++              .tx_bm_tkid             = 0x0c8,
++              .wpdma_rx_ring0         = 0x770,
++              .reset_idx_tx_mask      = GENMASK(1, 0),
++              .reset_idx_rx_mask      = GENMASK(7, 6),
++      },
++      .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc),
++};
++
+ static void
+ wed_m32(struct mtk_wed_device *dev, u32 reg, u32 mask, u32 val)
+ {
+@@ -746,7 +766,7 @@ mtk_wed_set_wpdma(struct mtk_wed_device
+               return;
+       wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
+-      wed_w32(dev, MTK_WED_WPDMA_RX_RING, dev->wlan.wpdma_rx);
++      wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx);
+ }
+ static void
+@@ -940,22 +960,10 @@ mtk_wed_hw_init(struct mtk_wed_device *d
+       wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
+       if (mtk_wed_is_v1(dev->hw)) {
+-              wed_w32(dev, MTK_WED_TX_BM_TKID,
+-                      FIELD_PREP(MTK_WED_TX_BM_TKID_START,
+-                                 dev->wlan.token_start) |
+-                      FIELD_PREP(MTK_WED_TX_BM_TKID_END,
+-                                 dev->wlan.token_start +
+-                                 dev->wlan.nbuf - 1));
+               wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
+                       FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) |
+                       MTK_WED_TX_BM_DYN_THR_HI);
+       } else {
+-              wed_w32(dev, MTK_WED_TX_BM_TKID_V2,
+-                      FIELD_PREP(MTK_WED_TX_BM_TKID_START,
+-                                 dev->wlan.token_start) |
+-                      FIELD_PREP(MTK_WED_TX_BM_TKID_END,
+-                                 dev->wlan.token_start +
+-                                 dev->wlan.nbuf - 1));
+               wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
+                       FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) |
+                       MTK_WED_TX_BM_DYN_THR_HI_V2);
+@@ -970,6 +978,11 @@ mtk_wed_hw_init(struct mtk_wed_device *d
+                       MTK_WED_TX_TKID_DYN_THR_HI);
+       }
++      wed_w32(dev, dev->hw->soc->regmap.tx_bm_tkid,
++              FIELD_PREP(MTK_WED_TX_BM_TKID_START, dev->wlan.token_start) |
++              FIELD_PREP(MTK_WED_TX_BM_TKID_END,
++                         dev->wlan.token_start + dev->wlan.nbuf - 1));
++
+       mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
+       if (mtk_wed_is_v1(dev->hw)) {
+@@ -1104,13 +1117,8 @@ mtk_wed_rx_reset(struct mtk_wed_device *
+       if (ret) {
+               mtk_wed_reset(dev, MTK_WED_RESET_WED_RX_DMA);
+       } else {
+-              struct mtk_eth *eth = dev->hw->eth;
+-
+-              if (mtk_is_netsys_v2_or_greater(eth))
+-                      wed_set(dev, MTK_WED_RESET_IDX,
+-                              MTK_WED_RESET_IDX_RX_V2);
+-              else
+-                      wed_set(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_RX);
++              wed_set(dev, MTK_WED_RESET_IDX,
++                      dev->hw->soc->regmap.reset_idx_rx_mask);
+               wed_w32(dev, MTK_WED_RESET_IDX, 0);
+       }
+@@ -1163,7 +1171,8 @@ mtk_wed_reset_dma(struct mtk_wed_device
+       if (busy) {
+               mtk_wed_reset(dev, MTK_WED_RESET_WED_TX_DMA);
+       } else {
+-              wed_w32(dev, MTK_WED_RESET_IDX, MTK_WED_RESET_IDX_TX);
++              wed_w32(dev, MTK_WED_RESET_IDX,
++                      dev->hw->soc->regmap.reset_idx_tx_mask);
+               wed_w32(dev, MTK_WED_RESET_IDX, 0);
+       }
+@@ -1255,7 +1264,6 @@ static int
+ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
+                          bool reset)
+ {
+-      u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
+       struct mtk_wed_ring *wdma;
+       if (idx >= ARRAY_SIZE(dev->rx_wdma))
+@@ -1263,7 +1271,7 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_we
+       wdma = &dev->rx_wdma[idx];
+       if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
+-                                       desc_size, true))
++                                       dev->hw->soc->wdma_desc_size, true))
+               return -ENOMEM;
+       wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
+@@ -1284,7 +1292,6 @@ static int
+ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev, int idx, int size,
+                          bool reset)
+ {
+-      u32 desc_size = sizeof(struct mtk_wdma_desc) * dev->hw->version;
+       struct mtk_wed_ring *wdma;
+       if (idx >= ARRAY_SIZE(dev->tx_wdma))
+@@ -1292,7 +1299,7 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we
+       wdma = &dev->tx_wdma[idx];
+       if (!reset && mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
+-                                       desc_size, true))
++                                       dev->hw->soc->wdma_desc_size, true))
+               return -ENOMEM;
+       wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
+@@ -1930,7 +1937,12 @@ void mtk_wed_add_hw(struct device_node *
+       hw->irq = irq;
+       hw->version = eth->soc->version;
+-      if (mtk_wed_is_v1(hw)) {
++      switch (hw->version) {
++      case 2:
++              hw->soc = &mt7986_data;
++              break;
++      default:
++      case 1:
+               hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
+                               "mediatek,pcie-mirror");
+               hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np,
+@@ -1944,6 +1956,8 @@ void mtk_wed_add_hw(struct device_node *
+                       regmap_write(hw->mirror, 0, 0);
+                       regmap_write(hw->mirror, 4, 0);
+               }
++              hw->soc = &mt7622_data;
++              break;
+       }
+       mtk_wed_hw_add_debugfs(hw);
+--- a/drivers/net/ethernet/mediatek/mtk_wed.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
+@@ -12,7 +12,18 @@
+ struct mtk_eth;
+ struct mtk_wed_wo;
++struct mtk_wed_soc_data {
++      struct {
++              u32 tx_bm_tkid;
++              u32 wpdma_rx_ring0;
++              u32 reset_idx_tx_mask;
++              u32 reset_idx_rx_mask;
++      } regmap;
++      u32 wdma_desc_size;
++};
++
+ struct mtk_wed_hw {
++      const struct mtk_wed_soc_data *soc;
+       struct device_node *node;
+       struct mtk_eth *eth;
+       struct regmap *regs;
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -100,8 +100,6 @@ struct mtk_wdma_desc {
+ #define MTK_WED_TX_BM_BASE                            0x084
+-#define MTK_WED_TX_BM_TKID                            0x088
+-#define MTK_WED_TX_BM_TKID_V2                         0x0c8
+ #define MTK_WED_TX_BM_TKID_START                      GENMASK(15, 0)
+ #define MTK_WED_TX_BM_TKID_END                                GENMASK(31, 16)
+@@ -160,9 +158,6 @@ struct mtk_wdma_desc {
+ #define MTK_WED_GLO_CFG_RX_2B_OFFSET                  BIT(31)
+ #define MTK_WED_RESET_IDX                             0x20c
+-#define MTK_WED_RESET_IDX_TX                          GENMASK(3, 0)
+-#define MTK_WED_RESET_IDX_RX                          GENMASK(17, 16)
+-#define MTK_WED_RESET_IDX_RX_V2                               GENMASK(7, 6)
+ #define MTK_WED_RESET_WPDMA_IDX_RX                    GENMASK(31, 30)
+ #define MTK_WED_TX_MIB(_n)                            (0x2a0 + (_n) * 4)
+@@ -286,7 +281,6 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX                        GENMASK(25, 24)
+ #define MTK_WED_WPDMA_RX_GLO_CFG                      0x76c
+-#define MTK_WED_WPDMA_RX_RING                         0x770
+ #define MTK_WED_WPDMA_RX_D_MIB(_n)                    (0x774 + (_n) * 4)
+ #define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n)          (0x784 + (_n) * 4)
diff --git a/target/linux/generic/backport-6.6/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch b/target/linux/generic/backport-6.6/752-14-v6.7-net-ethernet-mtk_wed-introduce-WED-support-for-MT798.patch
new file mode 100644 (file)
index 0000000..f565b76
--- /dev/null
@@ -0,0 +1,1280 @@
+From: Sujuan Chen <sujuan.chen@mediatek.com>
+Date: Mon, 18 Sep 2023 12:29:13 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: introduce WED support for MT7988
+
+Similar to MT7986 and MT7622, enable Wireless Ethernet Ditpatcher for
+MT7988 in order to offload traffic forwarded from LAN/WLAN to WLAN/LAN
+
+Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -197,6 +197,7 @@ static const struct mtk_reg_map mt7988_r
+       .wdma_base = {
+               [0]             = 0x4800,
+               [1]             = 0x4c00,
++              [2]             = 0x5000,
+       },
+       .pse_iq_sta             = 0x0180,
+       .pse_oq_sta             = 0x01a0,
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1132,7 +1132,7 @@ struct mtk_reg_map {
+       u32     gdm1_cnt;
+       u32     gdma_to_ppe;
+       u32     ppe_base;
+-      u32     wdma_base[2];
++      u32     wdma_base[3];
+       u32     pse_iq_sta;
+       u32     pse_oq_sta;
+ };
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -201,6 +201,9 @@ mtk_flow_set_output_device(struct mtk_et
+                       case 1:
+                               pse_port = PSE_WDMA1_PORT;
+                               break;
++                      case 2:
++                              pse_port = PSE_WDMA2_PORT;
++                              break;
+                       default:
+                               return -EINVAL;
+                       }
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -17,17 +17,19 @@
+ #include <net/flow_offload.h>
+ #include <net/pkt_cls.h>
+ #include "mtk_eth_soc.h"
+-#include "mtk_wed_regs.h"
+ #include "mtk_wed.h"
+ #include "mtk_ppe.h"
+ #include "mtk_wed_wo.h"
+ #define MTK_PCIE_BASE(n)              (0x1a143000 + (n) * 0x2000)
+-#define MTK_WED_PKT_SIZE              1900
++#define MTK_WED_PKT_SIZE              1920
+ #define MTK_WED_BUF_SIZE              2048
++#define MTK_WED_PAGE_BUF_SIZE         128
+ #define MTK_WED_BUF_PER_PAGE          (PAGE_SIZE / 2048)
++#define MTK_WED_RX_PAGE_BUF_PER_PAGE  (PAGE_SIZE / 128)
+ #define MTK_WED_RX_RING_SIZE          1536
++#define MTK_WED_RX_PG_BM_CNT          8192
+ #define MTK_WED_TX_RING_SIZE          2048
+ #define MTK_WED_WDMA_RING_SIZE                1024
+@@ -41,7 +43,10 @@
+ #define MTK_WED_RRO_QUE_CNT           8192
+ #define MTK_WED_MIOD_ENTRY_CNT                128
+-static struct mtk_wed_hw *hw_list[2];
++#define MTK_WED_TX_BM_DMA_SIZE                65536
++#define MTK_WED_TX_BM_PKT_CNT         32768
++
++static struct mtk_wed_hw *hw_list[3];
+ static DEFINE_MUTEX(hw_lock);
+ struct mtk_wed_flow_block_priv {
+@@ -56,6 +61,7 @@ static const struct mtk_wed_soc_data mt7
+               .reset_idx_tx_mask      = GENMASK(3, 0),
+               .reset_idx_rx_mask      = GENMASK(17, 16),
+       },
++      .tx_ring_desc_size = sizeof(struct mtk_wdma_desc),
+       .wdma_desc_size = sizeof(struct mtk_wdma_desc),
+ };
+@@ -66,6 +72,18 @@ static const struct mtk_wed_soc_data mt7
+               .reset_idx_tx_mask      = GENMASK(1, 0),
+               .reset_idx_rx_mask      = GENMASK(7, 6),
+       },
++      .tx_ring_desc_size = sizeof(struct mtk_wdma_desc),
++      .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc),
++};
++
++static const struct mtk_wed_soc_data mt7988_data = {
++      .regmap = {
++              .tx_bm_tkid             = 0x0c8,
++              .wpdma_rx_ring0         = 0x7d0,
++              .reset_idx_tx_mask      = GENMASK(1, 0),
++              .reset_idx_rx_mask      = GENMASK(7, 6),
++      },
++      .tx_ring_desc_size = sizeof(struct mtk_wed_bm_desc),
+       .wdma_desc_size = 2 * sizeof(struct mtk_wdma_desc),
+ };
+@@ -320,33 +338,38 @@ out:
+ static int
+ mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
+ {
++      u32 desc_size = dev->hw->soc->tx_ring_desc_size;
++      int i, page_idx = 0, n_pages, ring_size;
++      int token = dev->wlan.token_start;
+       struct mtk_wed_buf *page_list;
+-      struct mtk_wdma_desc *desc;
+       dma_addr_t desc_phys;
+-      int token = dev->wlan.token_start;
+-      int ring_size;
+-      int n_pages;
+-      int i, page_idx;
++      void *desc_ptr;
+-      ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
+-      n_pages = ring_size / MTK_WED_BUF_PER_PAGE;
++      if (!mtk_wed_is_v3_or_greater(dev->hw)) {
++              ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
++              dev->tx_buf_ring.size = ring_size;
++      } else {
++              dev->tx_buf_ring.size = MTK_WED_TX_BM_DMA_SIZE;
++              ring_size = MTK_WED_TX_BM_PKT_CNT;
++      }
++      n_pages = dev->tx_buf_ring.size / MTK_WED_BUF_PER_PAGE;
+       page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL);
+       if (!page_list)
+               return -ENOMEM;
+-      dev->tx_buf_ring.size = ring_size;
+       dev->tx_buf_ring.pages = page_list;
+-      desc = dma_alloc_coherent(dev->hw->dev, ring_size * sizeof(*desc),
+-                                &desc_phys, GFP_KERNEL);
+-      if (!desc)
++      desc_ptr = dma_alloc_coherent(dev->hw->dev,
++                                    dev->tx_buf_ring.size * desc_size,
++                                    &desc_phys, GFP_KERNEL);
++      if (!desc_ptr)
+               return -ENOMEM;
+-      dev->tx_buf_ring.desc = desc;
++      dev->tx_buf_ring.desc = desc_ptr;
+       dev->tx_buf_ring.desc_phys = desc_phys;
+-      for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) {
++      for (i = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) {
+               dma_addr_t page_phys, buf_phys;
+               struct page *page;
+               void *buf;
+@@ -372,28 +395,31 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
+               buf_phys = page_phys;
+               for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) {
+-                      u32 txd_size;
+-                      u32 ctrl;
+-
+-                      txd_size = dev->wlan.init_buf(buf, buf_phys, token++);
++                      struct mtk_wdma_desc *desc = desc_ptr;
+                       desc->buf0 = cpu_to_le32(buf_phys);
+-                      desc->buf1 = cpu_to_le32(buf_phys + txd_size);
++                      if (!mtk_wed_is_v3_or_greater(dev->hw)) {
++                              u32 txd_size, ctrl;
+-                      if (mtk_wed_is_v1(dev->hw))
+-                              ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
+-                                     FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
+-                                                MTK_WED_BUF_SIZE - txd_size) |
+-                                     MTK_WDMA_DESC_CTRL_LAST_SEG1;
+-                      else
+-                              ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size) |
+-                                     FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2,
+-                                                MTK_WED_BUF_SIZE - txd_size) |
+-                                     MTK_WDMA_DESC_CTRL_LAST_SEG0;
+-                      desc->ctrl = cpu_to_le32(ctrl);
+-                      desc->info = 0;
+-                      desc++;
++                              txd_size = dev->wlan.init_buf(buf, buf_phys,
++                                                            token++);
++                              desc->buf1 = cpu_to_le32(buf_phys + txd_size);
++                              ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0, txd_size);
++                              if (mtk_wed_is_v1(dev->hw))
++                                      ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG1 |
++                                              FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
++                                                         MTK_WED_BUF_SIZE - txd_size);
++                              else
++                                      ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG0 |
++                                              FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2,
++                                                         MTK_WED_BUF_SIZE - txd_size);
++                              desc->ctrl = cpu_to_le32(ctrl);
++                              desc->info = 0;
++                      } else {
++                              desc->ctrl = cpu_to_le32(token << 16);
++                      }
++                      desc_ptr += desc_size;
+                       buf += MTK_WED_BUF_SIZE;
+                       buf_phys += MTK_WED_BUF_SIZE;
+               }
+@@ -409,31 +435,31 @@ static void
+ mtk_wed_free_tx_buffer(struct mtk_wed_device *dev)
+ {
+       struct mtk_wed_buf *page_list = dev->tx_buf_ring.pages;
+-      struct mtk_wdma_desc *desc = dev->tx_buf_ring.desc;
+-      int page_idx;
+-      int i;
++      struct mtk_wed_hw *hw = dev->hw;
++      int i, page_idx = 0;
+       if (!page_list)
+               return;
+-      if (!desc)
++      if (!dev->tx_buf_ring.desc)
+               goto free_pagelist;
+-      for (i = 0, page_idx = 0; i < dev->tx_buf_ring.size;
+-           i += MTK_WED_BUF_PER_PAGE) {
+-              dma_addr_t buf_addr = page_list[page_idx].phy_addr;
++      for (i = 0; i < dev->tx_buf_ring.size; i += MTK_WED_BUF_PER_PAGE) {
++              dma_addr_t page_phy = page_list[page_idx].phy_addr;
+               void *page = page_list[page_idx++].p;
+               if (!page)
+                       break;
+-              dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE,
++              dma_unmap_page(dev->hw->dev, page_phy, PAGE_SIZE,
+                              DMA_BIDIRECTIONAL);
+               __free_page(page);
+       }
+-      dma_free_coherent(dev->hw->dev, dev->tx_buf_ring.size * sizeof(*desc),
+-                        desc, dev->tx_buf_ring.desc_phys);
++      dma_free_coherent(dev->hw->dev,
++                        dev->tx_buf_ring.size * hw->soc->tx_ring_desc_size,
++                        dev->tx_buf_ring.desc,
++                        dev->tx_buf_ring.desc_phys);
+ free_pagelist:
+       kfree(page_list);
+@@ -518,13 +544,23 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
+ {
+       u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
+-      if (mtk_wed_is_v1(dev->hw))
++      switch (dev->hw->version) {
++      case 1:
+               mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
+-      else
++              break;
++      case 2:
+               mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
+                       MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH |
+                       MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
+                       MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR;
++              break;
++      case 3:
++              mask = MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
++                     MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
++              break;
++      default:
++              break;
++      }
+       if (!dev->hw->num_flows)
+               mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
+@@ -536,6 +572,9 @@ mtk_wed_set_ext_int(struct mtk_wed_devic
+ static void
+ mtk_wed_set_512_support(struct mtk_wed_device *dev, bool enable)
+ {
++      if (!mtk_wed_is_v2(dev->hw))
++              return;
++
+       if (enable) {
+               wed_w32(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR);
+               wed_w32(dev, MTK_WED_TXP_DW1,
+@@ -610,6 +649,14 @@ mtk_wed_dma_disable(struct mtk_wed_devic
+                       MTK_WED_WPDMA_RX_D_RX_DRV_EN);
+               wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
+                       MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
++
++              if (mtk_wed_is_v3_or_greater(dev->hw) &&
++                  mtk_wed_get_rx_capa(dev)) {
++                      wdma_clr(dev, MTK_WDMA_PREF_TX_CFG,
++                               MTK_WDMA_PREF_TX_CFG_PREF_EN);
++                      wdma_clr(dev, MTK_WDMA_PREF_RX_CFG,
++                               MTK_WDMA_PREF_RX_CFG_PREF_EN);
++              }
+       }
+       mtk_wed_set_512_support(dev, false);
+@@ -651,6 +698,14 @@ mtk_wed_deinit(struct mtk_wed_device *de
+               MTK_WED_CTRL_RX_ROUTE_QM_EN |
+               MTK_WED_CTRL_WED_RX_BM_EN |
+               MTK_WED_CTRL_RX_RRO_QM_EN);
++
++      if (mtk_wed_is_v3_or_greater(dev->hw)) {
++              wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN);
++              wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_TX_AMSDU);
++              wed_clr(dev, MTK_WED_PCIE_INT_CTRL,
++                      MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
++                      MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER);
++      }
+ }
+ static void
+@@ -700,21 +755,37 @@ mtk_wed_detach(struct mtk_wed_device *de
+       mutex_unlock(&hw_lock);
+ }
+-#define PCIE_BASE_ADDR0               0x11280000
+ static void
+ mtk_wed_bus_init(struct mtk_wed_device *dev)
+ {
+       switch (dev->wlan.bus_type) {
+       case MTK_WED_BUS_PCIE: {
+               struct device_node *np = dev->hw->eth->dev->of_node;
+-              struct regmap *regs;
+-              regs = syscon_regmap_lookup_by_phandle(np,
+-                                                     "mediatek,wed-pcie");
+-              if (IS_ERR(regs))
+-                      break;
++              if (mtk_wed_is_v2(dev->hw)) {
++                      struct regmap *regs;
++
++                      regs = syscon_regmap_lookup_by_phandle(np,
++                                                             "mediatek,wed-pcie");
++                      if (IS_ERR(regs))
++                              break;
+-              regmap_update_bits(regs, 0, BIT(0), BIT(0));
++                      regmap_update_bits(regs, 0, BIT(0), BIT(0));
++              }
++
++              if (dev->wlan.msi) {
++                      wed_w32(dev, MTK_WED_PCIE_CFG_INTM,
++                              dev->hw->pcie_base | 0xc08);
++                      wed_w32(dev, MTK_WED_PCIE_CFG_BASE,
++                              dev->hw->pcie_base | 0xc04);
++                      wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(8));
++              } else {
++                      wed_w32(dev, MTK_WED_PCIE_CFG_INTM,
++                              dev->hw->pcie_base | 0x180);
++                      wed_w32(dev, MTK_WED_PCIE_CFG_BASE,
++                              dev->hw->pcie_base | 0x184);
++                      wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
++              }
+               wed_w32(dev, MTK_WED_PCIE_INT_CTRL,
+                       FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2));
+@@ -722,19 +793,9 @@ mtk_wed_bus_init(struct mtk_wed_device *
+               /* pcie interrupt control: pola/source selection */
+               wed_set(dev, MTK_WED_PCIE_INT_CTRL,
+                       MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
+-                      FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1));
+-              wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
+-
+-              wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180);
+-              wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184);
+-
+-              /* pcie interrupt status trigger register */
+-              wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
+-              wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER);
+-
+-              /* pola setting */
+-              wed_set(dev, MTK_WED_PCIE_INT_CTRL,
+-                      MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA);
++                      MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER  |
++                      FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL,
++                                 dev->hw->index));
+               break;
+       }
+       case MTK_WED_BUS_AXI:
+@@ -772,18 +833,19 @@ mtk_wed_set_wpdma(struct mtk_wed_device
+ static void
+ mtk_wed_hw_init_early(struct mtk_wed_device *dev)
+ {
+-      u32 mask, set;
++      u32 set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2);
++      u32 mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE;
+       mtk_wed_deinit(dev);
+       mtk_wed_reset(dev, MTK_WED_RESET_WED);
+       mtk_wed_set_wpdma(dev);
+-      mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE |
+-             MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE |
+-             MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE;
+-      set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2) |
+-            MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP |
+-            MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
++      if (!mtk_wed_is_v3_or_greater(dev->hw)) {
++              mask |= MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE |
++                      MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE;
++              set |= MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP |
++                     MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
++      }
+       wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set);
+       if (mtk_wed_is_v1(dev->hw)) {
+@@ -931,11 +993,18 @@ mtk_wed_route_qm_hw_init(struct mtk_wed_
+       }
+       /* configure RX_ROUTE_QM */
+-      wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
+-      wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT);
+-      wed_set(dev, MTK_WED_RTQM_GLO_CFG,
+-              FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT, 0x3 + dev->hw->index));
+-      wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
++      if (mtk_wed_is_v2(dev->hw)) {
++              wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
++              wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT);
++              wed_set(dev, MTK_WED_RTQM_GLO_CFG,
++                      FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT,
++                                 0x3 + dev->hw->index));
++              wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
++      } else {
++              wed_set(dev, MTK_WED_RTQM_ENQ_CFG0,
++                      FIELD_PREP(MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT,
++                                 0x3 + dev->hw->index));
++      }
+       /* enable RX_ROUTE_QM */
+       wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
+ }
+@@ -948,22 +1017,30 @@ mtk_wed_hw_init(struct mtk_wed_device *d
+       dev->init_done = true;
+       mtk_wed_set_ext_int(dev, false);
+-      wed_w32(dev, MTK_WED_TX_BM_CTRL,
+-              MTK_WED_TX_BM_CTRL_PAUSE |
+-              FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM,
+-                         dev->tx_buf_ring.size / 128) |
+-              FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM,
+-                         MTK_WED_TX_RING_SIZE / 256));
+       wed_w32(dev, MTK_WED_TX_BM_BASE, dev->tx_buf_ring.desc_phys);
+-
+       wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
+       if (mtk_wed_is_v1(dev->hw)) {
++              wed_w32(dev, MTK_WED_TX_BM_CTRL,
++                      MTK_WED_TX_BM_CTRL_PAUSE |
++                      FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM,
++                                 dev->tx_buf_ring.size / 128) |
++                      FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM,
++                                 MTK_WED_TX_RING_SIZE / 256));
+               wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
+                       FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, 1) |
+                       MTK_WED_TX_BM_DYN_THR_HI);
+-      } else {
++      } else if (mtk_wed_is_v2(dev->hw)) {
++              wed_w32(dev, MTK_WED_TX_BM_CTRL,
++                      MTK_WED_TX_BM_CTRL_PAUSE |
++                      FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM,
++                                 dev->tx_buf_ring.size / 128) |
++                      FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM,
++                                 MTK_WED_TX_RING_SIZE / 256));
++              wed_w32(dev, MTK_WED_TX_TKID_DYN_THR,
++                      FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) |
++                      MTK_WED_TX_TKID_DYN_THR_HI);
+               wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
+                       FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO_V2, 0) |
+                       MTK_WED_TX_BM_DYN_THR_HI_V2);
+@@ -973,9 +1050,6 @@ mtk_wed_hw_init(struct mtk_wed_device *d
+                                  dev->tx_buf_ring.size / 128) |
+                       FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM,
+                                  dev->tx_buf_ring.size / 128));
+-              wed_w32(dev, MTK_WED_TX_TKID_DYN_THR,
+-                      FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) |
+-                      MTK_WED_TX_TKID_DYN_THR_HI);
+       }
+       wed_w32(dev, dev->hw->soc->regmap.tx_bm_tkid,
+@@ -985,26 +1059,62 @@ mtk_wed_hw_init(struct mtk_wed_device *d
+       mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
++      if (mtk_wed_is_v3_or_greater(dev->hw)) {
++              /* switch to new bm architecture */
++              wed_clr(dev, MTK_WED_TX_BM_CTRL,
++                      MTK_WED_TX_BM_CTRL_LEGACY_EN);
++
++              wed_w32(dev, MTK_WED_TX_TKID_CTRL,
++                      MTK_WED_TX_TKID_CTRL_PAUSE |
++                      FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3,
++                                 dev->wlan.nbuf / 128) |
++                      FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3,
++                                 dev->wlan.nbuf / 128));
++              /* return SKBID + SDP back to bm */
++              wed_set(dev, MTK_WED_TX_TKID_CTRL,
++                      MTK_WED_TX_TKID_CTRL_FREE_FORMAT);
++
++              wed_w32(dev, MTK_WED_TX_BM_INIT_PTR,
++                      MTK_WED_TX_BM_PKT_CNT |
++                      MTK_WED_TX_BM_INIT_SW_TAIL_IDX);
++      }
++
+       if (mtk_wed_is_v1(dev->hw)) {
+               wed_set(dev, MTK_WED_CTRL,
+                       MTK_WED_CTRL_WED_TX_BM_EN |
+                       MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+-      } else {
+-              wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
+-              if (mtk_wed_get_rx_capa(dev)) {
+-                      /* rx hw init */
+-                      wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
+-                              MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
+-                              MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
+-                      wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
+-
+-                      mtk_wed_rx_buffer_hw_init(dev);
+-                      mtk_wed_rro_hw_init(dev);
+-                      mtk_wed_route_qm_hw_init(dev);
+-              }
++      } else if (mtk_wed_get_rx_capa(dev)) {
++              /* rx hw init */
++              wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
++                      MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
++                      MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
++              wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
++
++              /* reset prefetch index of ring */
++              wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX,
++                      MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
++              wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX,
++                      MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
++
++              wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX,
++                      MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
++              wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX,
++                      MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
++
++              /* reset prefetch FIFO of ring */
++              wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG,
++                      MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR |
++                      MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR);
++              wed_w32(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG, 0);
++
++              mtk_wed_rx_buffer_hw_init(dev);
++              mtk_wed_rro_hw_init(dev);
++              mtk_wed_route_qm_hw_init(dev);
+       }
+       wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE);
++      if (!mtk_wed_is_v1(dev->hw))
++              wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
+ }
+ static void
+@@ -1302,6 +1412,24 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_we
+                                        dev->hw->soc->wdma_desc_size, true))
+               return -ENOMEM;
++      if (mtk_wed_is_v3_or_greater(dev->hw)) {
++              struct mtk_wdma_desc *desc = wdma->desc;
++              int i;
++
++              for (i = 0; i < MTK_WED_WDMA_RING_SIZE; i++) {
++                      desc->buf0 = 0;
++                      desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
++                      desc->buf1 = 0;
++                      desc->info = cpu_to_le32(MTK_WDMA_TXD0_DESC_INFO_DMA_DONE);
++                      desc++;
++                      desc->buf0 = 0;
++                      desc->ctrl = cpu_to_le32(MTK_WDMA_DESC_CTRL_DMA_DONE);
++                      desc->buf1 = 0;
++                      desc->info = cpu_to_le32(MTK_WDMA_TXD1_DESC_INFO_DMA_DONE);
++                      desc++;
++              }
++      }
++
+       wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
+                wdma->desc_phys);
+       wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT,
+@@ -1367,6 +1495,9 @@ mtk_wed_configure_irq(struct mtk_wed_dev
+               wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
+       } else {
++              if (mtk_wed_is_v3_or_greater(dev->hw))
++                      wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_TKID_ALI_EN);
++
+               /* initail tx interrupt trigger */
+               wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
+                       MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
+@@ -1419,33 +1550,60 @@ mtk_wed_dma_enable(struct mtk_wed_device
+ {
+       int i;
+-      wed_set(dev, MTK_WED_WPDMA_INT_CTRL, MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
++      if (!mtk_wed_is_v3_or_greater(dev->hw)) {
++              wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
++                      MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
++              wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
++                      MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
++                      MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
++              wdma_set(dev, MTK_WDMA_GLO_CFG,
++                       MTK_WDMA_GLO_CFG_TX_DMA_EN |
++                       MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
++                       MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
++              wed_set(dev, MTK_WED_WPDMA_CTRL, MTK_WED_WPDMA_CTRL_SDL1_FIXED);
++      } else {
++              wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
++                      MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
++                      MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN |
++                      MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR);
++              wdma_set(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
++      }
+       wed_set(dev, MTK_WED_GLO_CFG,
+               MTK_WED_GLO_CFG_TX_DMA_EN |
+               MTK_WED_GLO_CFG_RX_DMA_EN);
+-      wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
+-              MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
+-              MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
++
+       wed_set(dev, MTK_WED_WDMA_GLO_CFG,
+               MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
+-      wdma_set(dev, MTK_WDMA_GLO_CFG,
+-               MTK_WDMA_GLO_CFG_TX_DMA_EN |
+-               MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
+-               MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
+-
+       if (mtk_wed_is_v1(dev->hw)) {
+               wdma_set(dev, MTK_WDMA_GLO_CFG,
+                        MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
+               return;
+       }
+-      wed_set(dev, MTK_WED_WPDMA_CTRL,
+-              MTK_WED_WPDMA_CTRL_SDL1_FIXED);
+       wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
+               MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
+               MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
++
++      if (mtk_wed_is_v3_or_greater(dev->hw)) {
++              wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG,
++                      FIELD_PREP(MTK_WED_WDMA_RX_PREF_BURST_SIZE, 0x10) |
++                      FIELD_PREP(MTK_WED_WDMA_RX_PREF_LOW_THRES, 0x8));
++              wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
++                      MTK_WED_WDMA_RX_PREF_DDONE2_EN);
++              wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, MTK_WED_WDMA_RX_PREF_EN);
++
++              wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
++                      MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST);
++              wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
++                      MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK |
++                      MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK |
++                      MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4);
++
++              wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
++      }
++
+       wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
+               MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
+               MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
+@@ -1457,11 +1615,22 @@ mtk_wed_dma_enable(struct mtk_wed_device
+               MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
+               MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
++      wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RXD_READ_LEN);
+       wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
+               MTK_WED_WPDMA_RX_D_RX_DRV_EN |
+               FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
+-              FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
+-                         0x2));
++              FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL, 0x2));
++
++      if (mtk_wed_is_v3_or_greater(dev->hw)) {
++              wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
++                      MTK_WED_WPDMA_RX_D_PREF_EN |
++                      FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE, 0x10) |
++                      FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_LOW_THRES, 0x8));
++
++              wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN);
++              wdma_set(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
++              wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
++      }
+       for (i = 0; i < MTK_WED_RX_QUEUES; i++)
+               mtk_wed_check_wfdma_rx_fill(dev, i);
+@@ -1501,6 +1670,12 @@ mtk_wed_start(struct mtk_wed_device *dev
+               wed_r32(dev, MTK_WED_EXT_INT_MASK1);
+               wed_r32(dev, MTK_WED_EXT_INT_MASK2);
++              if (mtk_wed_is_v3_or_greater(dev->hw)) {
++                      wed_w32(dev, MTK_WED_EXT_INT_MASK3,
++                              MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
++                      wed_r32(dev, MTK_WED_EXT_INT_MASK3);
++              }
++
+               if (mtk_wed_rro_cfg(dev))
+                       return;
+       }
+@@ -1552,6 +1727,7 @@ mtk_wed_attach(struct mtk_wed_device *de
+       dev->irq = hw->irq;
+       dev->wdma_idx = hw->index;
+       dev->version = hw->version;
++      dev->hw->pcie_base = mtk_wed_get_pcie_base(dev);
+       if (hw->eth->dma_dev == hw->eth->dev &&
+           of_dma_is_coherent(hw->eth->dev->of_node))
+@@ -1619,6 +1795,23 @@ mtk_wed_tx_ring_setup(struct mtk_wed_dev
+       ring->reg_base = MTK_WED_RING_TX(idx);
+       ring->wpdma = regs;
++      if (mtk_wed_is_v3_or_greater(dev->hw) && idx == 1) {
++              /* reset prefetch index */
++              wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG,
++                      MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR |
++                      MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR);
++
++              wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
++                      MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR |
++                      MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR);
++
++              /* reset prefetch FIFO */
++              wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG,
++                      MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR |
++                      MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR);
++              wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG, 0);
++      }
++
+       /* WED -> WPDMA */
+       wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys);
+       wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_COUNT, MTK_WED_TX_RING_SIZE);
+@@ -1693,15 +1886,13 @@ mtk_wed_rx_ring_setup(struct mtk_wed_dev
+ static u32
+ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
+ {
+-      u32 val, ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
++      u32 val, ext_mask;
+-      if (mtk_wed_is_v1(dev->hw))
+-              ext_mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
++      if (mtk_wed_is_v3_or_greater(dev->hw))
++              ext_mask = MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
++                         MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
+       else
+-              ext_mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH |
+-                          MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH |
+-                          MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
+-                          MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR;
++              ext_mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
+       val = wed_r32(dev, MTK_WED_EXT_INT_STATUS);
+       wed_w32(dev, MTK_WED_EXT_INT_STATUS, val);
+@@ -1941,6 +2132,9 @@ void mtk_wed_add_hw(struct device_node *
+       case 2:
+               hw->soc = &mt7986_data;
+               break;
++      case 3:
++              hw->soc = &mt7988_data;
++              break;
+       default:
+       case 1:
+               hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
+--- a/drivers/net/ethernet/mediatek/mtk_wed.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
+@@ -9,6 +9,8 @@
+ #include <linux/regmap.h>
+ #include <linux/netdevice.h>
++#include "mtk_wed_regs.h"
++
+ struct mtk_eth;
+ struct mtk_wed_wo;
+@@ -19,6 +21,7 @@ struct mtk_wed_soc_data {
+               u32 reset_idx_tx_mask;
+               u32 reset_idx_rx_mask;
+       } regmap;
++      u32 tx_ring_desc_size;
+       u32 wdma_desc_size;
+ };
+@@ -35,6 +38,7 @@ struct mtk_wed_hw {
+       struct dentry *debugfs_dir;
+       struct mtk_wed_device *wed_dev;
+       struct mtk_wed_wo *wed_wo;
++      u32 pcie_base;
+       u32 debugfs_reg;
+       u32 num_flows;
+       u8 version;
+@@ -61,6 +65,16 @@ static inline bool mtk_wed_is_v2(struct
+       return hw->version == 2;
+ }
++static inline bool mtk_wed_is_v3(struct mtk_wed_hw *hw)
++{
++      return hw->version == 3;
++}
++
++static inline bool mtk_wed_is_v3_or_greater(struct mtk_wed_hw *hw)
++{
++      return hw->version > 2;
++}
++
+ static inline void
+ wed_w32(struct mtk_wed_device *dev, u32 reg, u32 val)
+ {
+@@ -143,6 +157,21 @@ wpdma_txfree_w32(struct mtk_wed_device *
+       writel(val, dev->txfree_ring.wpdma + reg);
+ }
++static inline u32 mtk_wed_get_pcie_base(struct mtk_wed_device *dev)
++{
++      if (!mtk_wed_is_v3_or_greater(dev->hw))
++              return MTK_WED_PCIE_BASE;
++
++      switch (dev->hw->index) {
++      case 1:
++              return MTK_WED_PCIE_BASE1;
++      case 2:
++              return MTK_WED_PCIE_BASE2;
++      default:
++              return MTK_WED_PCIE_BASE0;
++      }
++}
++
+ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+                   void __iomem *wdma, phys_addr_t wdma_phy,
+                   int index);
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -331,10 +331,22 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
+               wo->hw->index + 1);
+       /* load firmware */
+-      if (of_device_is_compatible(wo->hw->node, "mediatek,mt7981-wed"))
+-              fw_name = MT7981_FIRMWARE_WO;
+-      else
+-              fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0;
++      switch (wo->hw->version) {
++      case 2:
++              if (of_device_is_compatible(wo->hw->node,
++                                          "mediatek,mt7981-wed"))
++                      fw_name = MT7981_FIRMWARE_WO;
++              else
++                      fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1
++                                              : MT7986_FIRMWARE_WO0;
++              break;
++      case 3:
++              fw_name = wo->hw->index ? MT7988_FIRMWARE_WO1
++                                      : MT7988_FIRMWARE_WO0;
++              break;
++      default:
++              return -EINVAL;
++      }
+       ret = request_firmware(&fw, fw_name, wo->hw->dev);
+       if (ret)
+@@ -355,15 +367,16 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
+       }
+       /* set the start address */
+-      boot_cr = wo->hw->index ? MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR
+-                              : MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
++      if (!mtk_wed_is_v3_or_greater(wo->hw) && wo->hw->index)
++              boot_cr = MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR;
++      else
++              boot_cr = MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
+       wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16);
+       /* wo firmware reset */
+       wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00);
+-      val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR);
+-      val |= wo->hw->index ? MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK
+-                           : MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK;
++      val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) |
++            MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK;
+       wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
+ out:
+       release_firmware(fw);
+@@ -398,3 +411,5 @@ int mtk_wed_mcu_init(struct mtk_wed_wo *
+ MODULE_FIRMWARE(MT7981_FIRMWARE_WO);
+ MODULE_FIRMWARE(MT7986_FIRMWARE_WO0);
+ MODULE_FIRMWARE(MT7986_FIRMWARE_WO1);
++MODULE_FIRMWARE(MT7988_FIRMWARE_WO0);
++MODULE_FIRMWARE(MT7988_FIRMWARE_WO1);
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -13,6 +13,9 @@
+ #define MTK_WDMA_DESC_CTRL_LAST_SEG0          BIT(30)
+ #define MTK_WDMA_DESC_CTRL_DMA_DONE           BIT(31)
++#define MTK_WDMA_TXD0_DESC_INFO_DMA_DONE      BIT(29)
++#define MTK_WDMA_TXD1_DESC_INFO_DMA_DONE      BIT(31)
++
+ struct mtk_wdma_desc {
+       __le32 buf0;
+       __le32 ctrl;
+@@ -37,6 +40,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RESET_WDMA_INT_AGENT                  BIT(19)
+ #define MTK_WED_RESET_RX_RRO_QM                               BIT(20)
+ #define MTK_WED_RESET_RX_ROUTE_QM                     BIT(21)
++#define MTK_WED_RESET_TX_AMSDU                                BIT(22)
+ #define MTK_WED_RESET_WED                             BIT(31)
+ #define MTK_WED_CTRL                                  0x00c
+@@ -44,6 +48,9 @@ struct mtk_wdma_desc {
+ #define MTK_WED_CTRL_WPDMA_INT_AGENT_BUSY             BIT(1)
+ #define MTK_WED_CTRL_WDMA_INT_AGENT_EN                        BIT(2)
+ #define MTK_WED_CTRL_WDMA_INT_AGENT_BUSY              BIT(3)
++#define MTK_WED_CTRL_WED_RX_IND_CMD_EN                        BIT(5)
++#define MTK_WED_CTRL_WED_RX_PG_BM_EN                  BIT(6)
++#define MTK_WED_CTRL_WED_RX_PG_BM_BUSY                        BIT(7)
+ #define MTK_WED_CTRL_WED_TX_BM_EN                     BIT(8)
+ #define MTK_WED_CTRL_WED_TX_BM_BUSY                   BIT(9)
+ #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN             BIT(10)
+@@ -54,9 +61,14 @@ struct mtk_wdma_desc {
+ #define MTK_WED_CTRL_RX_RRO_QM_BUSY                   BIT(15)
+ #define MTK_WED_CTRL_RX_ROUTE_QM_EN                   BIT(16)
+ #define MTK_WED_CTRL_RX_ROUTE_QM_BUSY                 BIT(17)
++#define MTK_WED_CTRL_TX_TKID_ALI_EN                   BIT(20)
++#define MTK_WED_CTRL_TX_TKID_ALI_BUSY                 BIT(21)
++#define MTK_WED_CTRL_TX_AMSDU_EN                      BIT(22)
++#define MTK_WED_CTRL_TX_AMSDU_BUSY                    BIT(23)
+ #define MTK_WED_CTRL_FINAL_DIDX_READ                  BIT(24)
+ #define MTK_WED_CTRL_ETH_DMAD_FMT                     BIT(25)
+ #define MTK_WED_CTRL_MIB_READ_CLEAR                   BIT(28)
++#define MTK_WED_CTRL_FLD_MIB_RD_CLR                   BIT(28)
+ #define MTK_WED_EXT_INT_STATUS                                0x020
+ #define MTK_WED_EXT_INT_STATUS_TF_LEN_ERR             BIT(0)
+@@ -89,6 +101,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_EXT_INT_MASK                          0x028
+ #define MTK_WED_EXT_INT_MASK1                         0x02c
+ #define MTK_WED_EXT_INT_MASK2                         0x030
++#define MTK_WED_EXT_INT_MASK3                         0x034
+ #define MTK_WED_STATUS                                        0x060
+ #define MTK_WED_STATUS_TX                             GENMASK(15, 8)
+@@ -96,9 +109,14 @@ struct mtk_wdma_desc {
+ #define MTK_WED_TX_BM_CTRL                            0x080
+ #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM                        GENMASK(6, 0)
+ #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM                        GENMASK(22, 16)
++#define MTK_WED_TX_BM_CTRL_LEGACY_EN                  BIT(26)
++#define MTK_WED_TX_TKID_CTRL_FREE_FORMAT              BIT(27)
+ #define MTK_WED_TX_BM_CTRL_PAUSE                      BIT(28)
+ #define MTK_WED_TX_BM_BASE                            0x084
++#define MTK_WED_TX_BM_INIT_PTR                                0x088
++#define MTK_WED_TX_BM_SW_TAIL_IDX                     GENMASK(16, 0)
++#define MTK_WED_TX_BM_INIT_SW_TAIL_IDX                        BIT(16)
+ #define MTK_WED_TX_BM_TKID_START                      GENMASK(15, 0)
+ #define MTK_WED_TX_BM_TKID_END                                GENMASK(31, 16)
+@@ -122,6 +140,9 @@ struct mtk_wdma_desc {
+ #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM              GENMASK(22, 16)
+ #define MTK_WED_TX_TKID_CTRL_PAUSE                    BIT(28)
++#define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3           GENMASK(7, 0)
++#define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3           GENMASK(23, 16)
++
+ #define MTK_WED_TX_TKID_DYN_THR                               0x0e0
+ #define MTK_WED_TX_TKID_DYN_THR_LO                    GENMASK(6, 0)
+ #define MTK_WED_TX_TKID_DYN_THR_HI                    GENMASK(22, 16)
+@@ -199,12 +220,15 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_PKT_PROC      BIT(5)
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC      BIT(6)
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_CRX_SYNC      BIT(7)
+-#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER        GENMASK(18, 16)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER        GENMASK(15, 12)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4  BIT(18)
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNSUPPORT_FMT    BIT(19)
+-#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UEVENT_PKT_FMT_CHK BIT(20)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK        BIT(20)
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR            BIT(21)
+ #define MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP            BIT(24)
++#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST               BIT(25)
+ #define MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV                BIT(28)
++#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK            BIT(30)
+ #define MTK_WED_WPDMA_RESET_IDX                               0x50c
+ #define MTK_WED_WPDMA_RESET_IDX_TX                    GENMASK(3, 0)
+@@ -250,9 +274,10 @@ struct mtk_wdma_desc {
+ #define MTK_WED_PCIE_INT_TRIGGER_STATUS                       BIT(16)
+ #define MTK_WED_PCIE_INT_CTRL                         0x57c
+-#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA             BIT(20)
+-#define MTK_WED_PCIE_INT_CTRL_SRC_SEL                 GENMASK(17, 16)
+ #define MTK_WED_PCIE_INT_CTRL_POLL_EN                 GENMASK(13, 12)
++#define MTK_WED_PCIE_INT_CTRL_SRC_SEL                 GENMASK(17, 16)
++#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA             BIT(20)
++#define MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER          BIT(21)
+ #define MTK_WED_WPDMA_CFG_BASE                                0x580
+ #define MTK_WED_WPDMA_CFG_INT_MASK                    0x584
+@@ -286,6 +311,20 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n)          (0x784 + (_n) * 4)
+ #define MTK_WED_WPDMA_RX_D_COHERENT_MIB                       0x78c
++#define MTK_WED_WPDMA_RX_D_PREF_CFG                   0x7b4
++#define MTK_WED_WPDMA_RX_D_PREF_EN                    BIT(0)
++#define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE            GENMASK(12, 8)
++#define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES             GENMASK(21, 16)
++
++#define MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX              0x7b8
++#define MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR          BIT(15)
++
++#define MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX              0x7bc
++
++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG              0x7c0
++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR               BIT(0)
++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR               BIT(16)
++
+ #define MTK_WED_WDMA_RING_TX                          0x800
+ #define MTK_WED_WDMA_TX_MIB                           0x810
+@@ -293,6 +332,18 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WDMA_RING_RX(_n)                      (0x900 + (_n) * 0x10)
+ #define MTK_WED_WDMA_RX_THRES(_n)                     (0x940 + (_n) * 0x4)
++#define MTK_WED_WDMA_RX_PREF_CFG                      0x950
++#define MTK_WED_WDMA_RX_PREF_EN                               BIT(0)
++#define MTK_WED_WDMA_RX_PREF_BURST_SIZE                       GENMASK(12, 8)
++#define MTK_WED_WDMA_RX_PREF_LOW_THRES                        GENMASK(21, 16)
++#define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR             BIT(24)
++#define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR             BIT(25)
++#define MTK_WED_WDMA_RX_PREF_DDONE2_EN                        BIT(26)
++
++#define MTK_WED_WDMA_RX_PREF_FIFO_CFG                 0x95C
++#define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR             BIT(0)
++#define MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR             BIT(16)
++
+ #define MTK_WED_WDMA_GLO_CFG                          0xa04
+ #define MTK_WED_WDMA_GLO_CFG_TX_DRV_EN                        BIT(0)
+ #define MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK             BIT(1)
+@@ -325,6 +376,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WDMA_INT_TRIGGER_RX_DONE              GENMASK(17, 16)
+ #define MTK_WED_WDMA_INT_CTRL                         0xa2c
++#define MTK_WED_WDMA_INT_POLL_PRD                     GENMASK(7, 0)
+ #define MTK_WED_WDMA_INT_CTRL_POLL_SRC_SEL            GENMASK(17, 16)
+ #define MTK_WED_WDMA_CFG_BASE                         0xaa0
+@@ -388,6 +440,18 @@ struct mtk_wdma_desc {
+ #define MTK_WDMA_INT_GRP1                             0x250
+ #define MTK_WDMA_INT_GRP2                             0x254
++#define MTK_WDMA_PREF_TX_CFG                          0x2d0
++#define MTK_WDMA_PREF_TX_CFG_PREF_EN                  BIT(0)
++
++#define MTK_WDMA_PREF_RX_CFG                          0x2dc
++#define MTK_WDMA_PREF_RX_CFG_PREF_EN                  BIT(0)
++
++#define MTK_WDMA_WRBK_TX_CFG                          0x300
++#define MTK_WDMA_WRBK_TX_CFG_WRBK_EN                  BIT(30)
++
++#define MTK_WDMA_WRBK_RX_CFG                          0x344
++#define MTK_WDMA_WRBK_RX_CFG_WRBK_EN                  BIT(30)
++
+ #define MTK_PCIE_MIRROR_MAP(n)                                ((n) ? 0x4 : 0x0)
+ #define MTK_PCIE_MIRROR_MAP_EN                                BIT(0)
+ #define MTK_PCIE_MIRROR_MAP_WED_ID                    BIT(1)
+@@ -401,6 +465,30 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RTQM_Q_DBG_BYPASS                     BIT(5)
+ #define MTK_WED_RTQM_TXDMAD_FPORT                     GENMASK(23, 20)
++#define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT              0xb1c
++#define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n)           (0xb20 + (_n) * 0x4)
++#define       MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT                 0xb28
++#define MTK_WED_RTQM_IGRS0_I2H_PKT_CNT(_n)            (0xb2c + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS0_FDROP_CNT                  0xb34
++
++#define MTK_WED_RTQM_IGRS1_I2HW_DMAD_CNT              0xb44
++#define MTK_WED_RTQM_IGRS1_I2H_DMAD_CNT(_n)           (0xb48 + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS1_I2HW_PKT_CNT                       0xb50
++#define MTK_WED_RTQM_IGRS1_I2H_PKT_CNT(_n)            (0xb54 + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS1_FDROP_CNT                  0xb5c
++
++#define MTK_WED_RTQM_IGRS2_I2HW_DMAD_CNT              0xb6c
++#define MTK_WED_RTQM_IGRS2_I2H_DMAD_CNT(_n)           (0xb70 + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS2_I2HW_PKT_CNT                       0xb78
++#define MTK_WED_RTQM_IGRS2_I2H_PKT_CNT(_n)            (0xb7c + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS2_FDROP_CNT                  0xb84
++
++#define MTK_WED_RTQM_IGRS3_I2HW_DMAD_CNT              0xb94
++#define MTK_WED_RTQM_IGRS3_I2H_DMAD_CNT(_n)           (0xb98 + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS3_I2HW_PKT_CNT                       0xba0
++#define MTK_WED_RTQM_IGRS3_I2H_PKT_CNT(_n)            (0xba4 + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS3_FDROP_CNT                  0xbac
++
+ #define MTK_WED_RTQM_R2H_MIB(_n)                      (0xb70 + (_n) * 0x4)
+ #define MTK_WED_RTQM_R2Q_MIB(_n)                      (0xb78 + (_n) * 0x4)
+ #define MTK_WED_RTQM_Q2N_MIB                          0xb80
+@@ -409,6 +497,24 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RTQM_Q2B_MIB                          0xb8c
+ #define MTK_WED_RTQM_PFDBK_MIB                                0xb90
++#define MTK_WED_RTQM_ENQ_CFG0                         0xbb8
++#define MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT             GENMASK(15, 12)
++
++#define MTK_WED_RTQM_FDROP_MIB                                0xb84
++#define MTK_WED_RTQM_ENQ_I2Q_DMAD_CNT                 0xbbc
++#define MTK_WED_RTQM_ENQ_I2N_DMAD_CNT                 0xbc0
++#define MTK_WED_RTQM_ENQ_I2Q_PKT_CNT                  0xbc4
++#define MTK_WED_RTQM_ENQ_I2N_PKT_CNT                  0xbc8
++#define MTK_WED_RTQM_ENQ_USED_ENTRY_CNT                       0xbcc
++#define MTK_WED_RTQM_ENQ_ERR_CNT                      0xbd0
++
++#define MTK_WED_RTQM_DEQ_DMAD_CNT                     0xbd8
++#define MTK_WED_RTQM_DEQ_Q2I_DMAD_CNT                 0xbdc
++#define MTK_WED_RTQM_DEQ_PKT_CNT                      0xbe0
++#define MTK_WED_RTQM_DEQ_Q2I_PKT_CNT                  0xbe4
++#define MTK_WED_RTQM_DEQ_USED_PFDBK_CNT                       0xbe8
++#define MTK_WED_RTQM_DEQ_ERR_CNT                      0xbec
++
+ #define MTK_WED_RROQM_GLO_CFG                         0xc04
+ #define MTK_WED_RROQM_RST_IDX                         0xc08
+ #define MTK_WED_RROQM_RST_IDX_MIOD                    BIT(0)
+@@ -458,7 +564,116 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RX_BM_INTF                            0xd9c
+ #define MTK_WED_RX_BM_ERR_STS                         0xda8
++#define MTK_RRO_IND_CMD_SIGNATURE                     0xe00
++#define MTK_RRO_IND_CMD_DMA_IDX                               GENMASK(11, 0)
++#define MTK_RRO_IND_CMD_MAGIC_CNT                     GENMASK(30, 28)
++
++#define MTK_WED_IND_CMD_RX_CTRL0                      0xe04
++#define MTK_WED_IND_CMD_PROC_IDX                      GENMASK(11, 0)
++#define MTK_WED_IND_CMD_PREFETCH_FREE_CNT             GENMASK(19, 16)
++#define MTK_WED_IND_CMD_MAGIC_CNT                     GENMASK(30, 28)
++
++#define MTK_WED_IND_CMD_RX_CTRL1                      0xe08
++#define MTK_WED_IND_CMD_RX_CTRL2                      0xe0c
++#define MTK_WED_IND_CMD_MAX_CNT                               GENMASK(11, 0)
++#define MTK_WED_IND_CMD_BASE_M                                GENMASK(19, 16)
++
++#define MTK_WED_RRO_CFG0                              0xe10
++#define MTK_WED_RRO_CFG1                              0xe14
++#define MTK_WED_RRO_CFG1_MAX_WIN_SZ                   GENMASK(31, 29)
++#define MTK_WED_RRO_CFG1_ACK_SN_BASE_M                        GENMASK(19, 16)
++#define MTK_WED_RRO_CFG1_PARTICL_SE_ID                        GENMASK(11, 0)
++
++#define MTK_WED_ADDR_ELEM_CFG0                                0xe18
++#define MTK_WED_ADDR_ELEM_CFG1                                0xe1c
++#define MTK_WED_ADDR_ELEM_PREFETCH_FREE_CNT           GENMASK(19, 16)
++
++#define MTK_WED_ADDR_ELEM_TBL_CFG                     0xe20
++#define MTK_WED_ADDR_ELEM_TBL_OFFSET                  GENMASK(6, 0)
++#define MTK_WED_ADDR_ELEM_TBL_RD_RDY                  BIT(28)
++#define MTK_WED_ADDR_ELEM_TBL_WR_RDY                  BIT(29)
++#define MTK_WED_ADDR_ELEM_TBL_RD                      BIT(30)
++#define MTK_WED_ADDR_ELEM_TBL_WR                      BIT(31)
++
++#define MTK_WED_RADDR_ELEM_TBL_WDATA                  0xe24
++#define MTK_WED_RADDR_ELEM_TBL_RDATA                  0xe28
++
++#define MTK_WED_PN_CHECK_CFG                          0xe30
++#define MTK_WED_PN_CHECK_SE_ID                                GENMASK(11, 0)
++#define MTK_WED_PN_CHECK_RD_RDY                               BIT(28)
++#define MTK_WED_PN_CHECK_WR_RDY                               BIT(29)
++#define MTK_WED_PN_CHECK_RD                           BIT(30)
++#define MTK_WED_PN_CHECK_WR                           BIT(31)
++
++#define MTK_WED_PN_CHECK_WDATA_M                      0xe38
++#define MTK_WED_PN_CHECK_IS_FIRST                     BIT(17)
++
++#define MTK_WED_RRO_MSDU_PG_RING_CFG(_n)              (0xe44 + (_n) * 0x8)
++
++#define MTK_WED_RRO_MSDU_PG_RING2_CFG                 0xe58
++#define MTK_WED_RRO_MSDU_PG_DRV_CLR                   BIT(26)
++#define MTK_WED_RRO_MSDU_PG_DRV_EN                    BIT(31)
++
++#define MTK_WED_RRO_MSDU_PG_CTRL0(_n)                 (0xe5c + (_n) * 0xc)
++#define MTK_WED_RRO_MSDU_PG_CTRL1(_n)                 (0xe60 + (_n) * 0xc)
++#define MTK_WED_RRO_MSDU_PG_CTRL2(_n)                 (0xe64 + (_n) * 0xc)
++
++#define MTK_WED_RRO_RX_D_RX(_n)                               (0xe80 + (_n) * 0x10)
++
++#define MTK_WED_RRO_RX_MAGIC_CNT                      BIT(13)
++
++#define MTK_WED_RRO_RX_D_CFG(_n)                      (0xea0 + (_n) * 0x4)
++#define MTK_WED_RRO_RX_D_DRV_CLR                      BIT(26)
++#define MTK_WED_RRO_RX_D_DRV_EN                               BIT(31)
++
++#define MTK_WED_RRO_PG_BM_RX_DMAM                     0xeb0
++#define MTK_WED_RRO_PG_BM_RX_SDL0                     GENMASK(13, 0)
++
++#define MTK_WED_RRO_PG_BM_BASE                                0xeb4
++#define MTK_WED_RRO_PG_BM_INIT_PTR                    0xeb8
++#define MTK_WED_RRO_PG_BM_SW_TAIL_IDX                 GENMASK(15, 0)
++#define MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX            BIT(16)
++
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX                 0xeec
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN             BIT(0)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR            BIT(1)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG      GENMASK(6, 2)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN             BIT(8)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR            BIT(9)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG      GENMASK(14, 10)
++
++#define MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG            0xef4
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN             BIT(0)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR            BIT(1)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG      GENMASK(6, 2)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN             BIT(8)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR            BIT(9)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG      GENMASK(14, 10)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN             BIT(16)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR            BIT(17)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG      GENMASK(22, 18)
++
++#define MTK_WED_RX_IND_CMD_CNT0                               0xf20
++#define MTK_WED_RX_IND_CMD_DBG_CNT_EN                 BIT(31)
++
++#define MTK_WED_RX_IND_CMD_CNT(_n)                    (0xf20 + (_n) * 0x4)
++#define MTK_WED_IND_CMD_MAGIC_CNT_FAIL_CNT            GENMASK(15, 0)
++
++#define MTK_WED_RX_ADDR_ELEM_CNT(_n)                  (0xf48 + (_n) * 0x4)
++#define MTK_WED_ADDR_ELEM_SIG_FAIL_CNT                        GENMASK(15, 0)
++#define MTK_WED_ADDR_ELEM_FIRST_SIG_FAIL_CNT          GENMASK(31, 16)
++#define MTK_WED_ADDR_ELEM_ACKSN_CNT                   GENMASK(27, 0)
++
++#define MTK_WED_RX_MSDU_PG_CNT(_n)                    (0xf5c + (_n) * 0x4)
++
++#define MTK_WED_RX_PN_CHK_CNT                         0xf70
++#define MTK_WED_PN_CHK_FAIL_CNT                               GENMASK(15, 0)
++
+ #define MTK_WED_WOCPU_VIEW_MIOD_BASE                  0x8000
+ #define MTK_WED_PCIE_INT_MASK                         0x0
++#define MTK_WED_PCIE_BASE                     0x11280000
++#define MTK_WED_PCIE_BASE0                    0x11300000
++#define MTK_WED_PCIE_BASE1                    0x11310000
++#define MTK_WED_PCIE_BASE2                    0x11290000
+ #endif
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
+@@ -91,6 +91,8 @@ enum mtk_wed_dummy_cr_idx {
+ #define MT7981_FIRMWARE_WO    "mediatek/mt7981_wo.bin"
+ #define MT7986_FIRMWARE_WO0   "mediatek/mt7986_wo_0.bin"
+ #define MT7986_FIRMWARE_WO1   "mediatek/mt7986_wo_1.bin"
++#define MT7988_FIRMWARE_WO0   "mediatek/mt7988_wo_0.bin"
++#define MT7988_FIRMWARE_WO1   "mediatek/mt7988_wo_1.bin"
+ #define MTK_WO_MCU_CFG_LS_BASE                                0
+ #define MTK_WO_MCU_CFG_LS_HW_VER_ADDR                 (MTK_WO_MCU_CFG_LS_BASE + 0x000)
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -138,6 +138,8 @@ struct mtk_wed_device {
+               u32 wpdma_rx;
+               bool wcid_512;
++              bool hw_rro;
++              bool msi;
+               u16 token_start;
+               unsigned int nbuf;
+@@ -211,10 +213,12 @@ mtk_wed_device_attach(struct mtk_wed_dev
+       return ret;
+ }
+-static inline bool
+-mtk_wed_get_rx_capa(struct mtk_wed_device *dev)
++static inline bool mtk_wed_get_rx_capa(struct mtk_wed_device *dev)
+ {
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
++      if (dev->version == 3)
++              return dev->wlan.hw_rro;
++
+       return dev->version != 1;
+ #else
+       return false;
diff --git a/target/linux/generic/backport-6.6/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch b/target/linux/generic/backport-6.6/752-15-v6.7-net-ethernet-mtk_wed-refactor-mtk_wed_check_wfdma_rx.patch
new file mode 100644 (file)
index 0000000..aa2f952
--- /dev/null
@@ -0,0 +1,95 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Sep 2023 12:29:14 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: refactor mtk_wed_check_wfdma_rx_fill
+ routine
+
+Refactor mtk_wed_check_wfdma_rx_fill() in order to be reused adding HW
+receive offload support for MT7988 SoC.
+
+Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -586,22 +586,15 @@ mtk_wed_set_512_support(struct mtk_wed_d
+       }
+ }
+-#define MTK_WFMDA_RX_DMA_EN   BIT(2)
+-static void
+-mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx)
++static int
++mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev,
++                          struct mtk_wed_ring *ring)
+ {
+-      u32 val;
+       int i;
+-      if (!(dev->rx_ring[idx].flags & MTK_WED_RING_CONFIGURED))
+-              return; /* queue is not configured by mt76 */
+-
+       for (i = 0; i < 3; i++) {
+-              u32 cur_idx;
++              u32 cur_idx = readl(ring->wpdma + MTK_WED_RING_OFS_CPU_IDX);
+-              cur_idx = wed_r32(dev,
+-                                MTK_WED_WPDMA_RING_RX_DATA(idx) +
+-                                MTK_WED_RING_OFS_CPU_IDX);
+               if (cur_idx == MTK_WED_RX_RING_SIZE - 1)
+                       break;
+@@ -610,12 +603,10 @@ mtk_wed_check_wfdma_rx_fill(struct mtk_w
+       if (i == 3) {
+               dev_err(dev->hw->dev, "rx dma enable failed\n");
+-              return;
++              return -ETIMEDOUT;
+       }
+-      val = wifi_r32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base) |
+-            MTK_WFMDA_RX_DMA_EN;
+-      wifi_w32(dev, dev->wlan.wpdma_rx_glo - dev->wlan.phy_base, val);
++      return 0;
+ }
+ static void
+@@ -1545,6 +1536,7 @@ mtk_wed_configure_irq(struct mtk_wed_dev
+       wed_w32(dev, MTK_WED_INT_MASK, irq_mask);
+ }
++#define MTK_WFMDA_RX_DMA_EN   BIT(2)
+ static void
+ mtk_wed_dma_enable(struct mtk_wed_device *dev)
+ {
+@@ -1632,8 +1624,26 @@ mtk_wed_dma_enable(struct mtk_wed_device
+               wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
+       }
+-      for (i = 0; i < MTK_WED_RX_QUEUES; i++)
+-              mtk_wed_check_wfdma_rx_fill(dev, i);
++      for (i = 0; i < MTK_WED_RX_QUEUES; i++) {
++              struct mtk_wed_ring *ring = &dev->rx_ring[i];
++              u32 val;
++
++              if (!(ring->flags & MTK_WED_RING_CONFIGURED))
++                      continue; /* queue is not configured by mt76 */
++
++              if (mtk_wed_check_wfdma_rx_fill(dev, ring)) {
++                      dev_err(dev->hw->dev,
++                              "rx_ring(%d) dma enable failed\n", i);
++                      continue;
++              }
++
++              val = wifi_r32(dev,
++                             dev->wlan.wpdma_rx_glo -
++                             dev->wlan.phy_base) | MTK_WFMDA_RX_DMA_EN;
++              wifi_w32(dev,
++                       dev->wlan.wpdma_rx_glo - dev->wlan.phy_base,
++                       val);
++      }
+ }
+ static void
diff --git a/target/linux/generic/backport-6.6/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch b/target/linux/generic/backport-6.6/752-16-v6.7-net-ethernet-mtk_wed-introduce-partial-AMSDU-offload.patch
new file mode 100644 (file)
index 0000000..4e72ea1
--- /dev/null
@@ -0,0 +1,465 @@
+From: Sujuan Chen <sujuan.chen@mediatek.com>
+Date: Mon, 18 Sep 2023 12:29:15 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: introduce partial AMSDU offload
+ support for MT7988
+
+Introduce partial AMSDU offload support for MT7988 SoC in order to merge
+in hw packets belonging to the same AMSDU before passing them to the
+WLAN nic.
+
+Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -439,7 +439,8 @@ int mtk_foe_entry_set_pppoe(struct mtk_e
+ }
+ int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
+-                         int wdma_idx, int txq, int bss, int wcid)
++                         int wdma_idx, int txq, int bss, int wcid,
++                         bool amsdu_en)
+ {
+       struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(eth, entry);
+       u32 *ib2 = mtk_foe_entry_ib2(eth, entry);
+@@ -451,6 +452,7 @@ int mtk_foe_entry_set_wdma(struct mtk_et
+                        MTK_FOE_IB2_WDMA_WINFO_V2;
+               l2->w3info = FIELD_PREP(MTK_FOE_WINFO_WCID_V3, wcid) |
+                            FIELD_PREP(MTK_FOE_WINFO_BSS_V3, bss);
++              l2->amsdu = FIELD_PREP(MTK_FOE_WINFO_AMSDU_EN, amsdu_en);
+               break;
+       case 2:
+               *ib2 &= ~MTK_FOE_IB2_PORT_MG_V2;
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -88,13 +88,13 @@ enum {
+ #define MTK_FOE_WINFO_BSS_V3          GENMASK(23, 16)
+ #define MTK_FOE_WINFO_WCID_V3         GENMASK(15, 0)
+-#define MTK_FOE_WINFO_PAO_USR_INFO    GENMASK(15, 0)
+-#define MTK_FOE_WINFO_PAO_TID         GENMASK(19, 16)
+-#define MTK_FOE_WINFO_PAO_IS_FIXEDRATE        BIT(20)
+-#define MTK_FOE_WINFO_PAO_IS_PRIOR    BIT(21)
+-#define MTK_FOE_WINFO_PAO_IS_SP               BIT(22)
+-#define MTK_FOE_WINFO_PAO_HF          BIT(23)
+-#define MTK_FOE_WINFO_PAO_AMSDU_EN    BIT(24)
++#define MTK_FOE_WINFO_AMSDU_USR_INFO  GENMASK(15, 0)
++#define MTK_FOE_WINFO_AMSDU_TID               GENMASK(19, 16)
++#define MTK_FOE_WINFO_AMSDU_IS_FIXEDRATE      BIT(20)
++#define MTK_FOE_WINFO_AMSDU_IS_PRIOR  BIT(21)
++#define MTK_FOE_WINFO_AMSDU_IS_SP     BIT(22)
++#define MTK_FOE_WINFO_AMSDU_HF                BIT(23)
++#define MTK_FOE_WINFO_AMSDU_EN                BIT(24)
+ enum {
+       MTK_FOE_STATE_INVALID,
+@@ -123,7 +123,7 @@ struct mtk_foe_mac_info {
+       /* netsys_v3 */
+       u32 w3info;
+-      u32 wpao;
++      u32 amsdu;
+ };
+ /* software-only entry type */
+@@ -394,7 +394,8 @@ int mtk_foe_entry_set_vlan(struct mtk_et
+ int mtk_foe_entry_set_pppoe(struct mtk_eth *eth, struct mtk_foe_entry *entry,
+                           int sid);
+ int mtk_foe_entry_set_wdma(struct mtk_eth *eth, struct mtk_foe_entry *entry,
+-                         int wdma_idx, int txq, int bss, int wcid);
++                         int wdma_idx, int txq, int bss, int wcid,
++                         bool amsdu_en);
+ int mtk_foe_entry_set_queue(struct mtk_eth *eth, struct mtk_foe_entry *entry,
+                           unsigned int queue);
+ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -111,6 +111,7 @@ mtk_flow_get_wdma_info(struct net_device
+       info->queue = path->mtk_wdma.queue;
+       info->bss = path->mtk_wdma.bss;
+       info->wcid = path->mtk_wdma.wcid;
++      info->amsdu = path->mtk_wdma.amsdu;
+       return 0;
+ }
+@@ -192,7 +193,7 @@ mtk_flow_set_output_device(struct mtk_et
+       if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
+               mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue,
+-                                     info.bss, info.wcid);
++                                     info.bss, info.wcid, info.amsdu);
+               if (mtk_is_netsys_v2_or_greater(eth)) {
+                       switch (info.wdma_idx) {
+                       case 0:
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -30,6 +30,8 @@
+ #define MTK_WED_RX_PAGE_BUF_PER_PAGE  (PAGE_SIZE / 128)
+ #define MTK_WED_RX_RING_SIZE          1536
+ #define MTK_WED_RX_PG_BM_CNT          8192
++#define MTK_WED_AMSDU_BUF_SIZE                (PAGE_SIZE << 4)
++#define MTK_WED_AMSDU_NPAGES          32
+ #define MTK_WED_TX_RING_SIZE          2048
+ #define MTK_WED_WDMA_RING_SIZE                1024
+@@ -173,6 +175,23 @@ mtk_wdma_rx_reset(struct mtk_wed_device
+       return ret;
+ }
++static u32
++mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
++{
++      return !!(wed_r32(dev, reg) & mask);
++}
++
++static int
++mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
++{
++      int sleep = 15000;
++      int timeout = 100 * sleep;
++      u32 val;
++
++      return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
++                               timeout, false, dev, reg, mask);
++}
++
+ static void
+ mtk_wdma_tx_reset(struct mtk_wed_device *dev)
+ {
+@@ -336,6 +355,118 @@ out:
+ }
+ static int
++mtk_wed_amsdu_buffer_alloc(struct mtk_wed_device *dev)
++{
++      struct mtk_wed_hw *hw = dev->hw;
++      struct mtk_wed_amsdu *wed_amsdu;
++      int i;
++
++      if (!mtk_wed_is_v3_or_greater(hw))
++              return 0;
++
++      wed_amsdu = devm_kcalloc(hw->dev, MTK_WED_AMSDU_NPAGES,
++                               sizeof(*wed_amsdu), GFP_KERNEL);
++      if (!wed_amsdu)
++              return -ENOMEM;
++
++      for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) {
++              void *ptr;
++
++              /* each segment is 64K */
++              ptr = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN |
++                                             __GFP_ZERO | __GFP_COMP |
++                                             GFP_DMA32,
++                                             get_order(MTK_WED_AMSDU_BUF_SIZE));
++              if (!ptr)
++                      goto error;
++
++              wed_amsdu[i].txd = ptr;
++              wed_amsdu[i].txd_phy = dma_map_single(hw->dev, ptr,
++                                                    MTK_WED_AMSDU_BUF_SIZE,
++                                                    DMA_TO_DEVICE);
++              if (dma_mapping_error(hw->dev, wed_amsdu[i].txd_phy))
++                      goto error;
++      }
++      dev->hw->wed_amsdu = wed_amsdu;
++
++      return 0;
++
++error:
++      for (i--; i >= 0; i--)
++              dma_unmap_single(hw->dev, wed_amsdu[i].txd_phy,
++                               MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE);
++      return -ENOMEM;
++}
++
++static void
++mtk_wed_amsdu_free_buffer(struct mtk_wed_device *dev)
++{
++      struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu;
++      int i;
++
++      if (!wed_amsdu)
++              return;
++
++      for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++) {
++              dma_unmap_single(dev->hw->dev, wed_amsdu[i].txd_phy,
++                               MTK_WED_AMSDU_BUF_SIZE, DMA_TO_DEVICE);
++              free_pages((unsigned long)wed_amsdu[i].txd,
++                         get_order(MTK_WED_AMSDU_BUF_SIZE));
++      }
++}
++
++static int
++mtk_wed_amsdu_init(struct mtk_wed_device *dev)
++{
++      struct mtk_wed_amsdu *wed_amsdu = dev->hw->wed_amsdu;
++      int i, ret;
++
++      if (!wed_amsdu)
++              return 0;
++
++      for (i = 0; i < MTK_WED_AMSDU_NPAGES; i++)
++              wed_w32(dev, MTK_WED_AMSDU_HIFTXD_BASE_L(i),
++                      wed_amsdu[i].txd_phy);
++
++      /* init all sta parameter */
++      wed_w32(dev, MTK_WED_AMSDU_STA_INFO_INIT, MTK_WED_AMSDU_STA_RMVL |
++              MTK_WED_AMSDU_STA_WTBL_HDRT_MODE |
++              FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_LEN,
++                         dev->wlan.amsdu_max_len >> 8) |
++              FIELD_PREP(MTK_WED_AMSDU_STA_MAX_AMSDU_NUM,
++                         dev->wlan.amsdu_max_subframes));
++
++      wed_w32(dev, MTK_WED_AMSDU_STA_INFO, MTK_WED_AMSDU_STA_INFO_DO_INIT);
++
++      ret = mtk_wed_poll_busy(dev, MTK_WED_AMSDU_STA_INFO,
++                              MTK_WED_AMSDU_STA_INFO_DO_INIT);
++      if (ret) {
++              dev_err(dev->hw->dev, "amsdu initialization failed\n");
++              return ret;
++      }
++
++      /* init partial amsdu offload txd src */
++      wed_set(dev, MTK_WED_AMSDU_HIFTXD_CFG,
++              FIELD_PREP(MTK_WED_AMSDU_HIFTXD_SRC, dev->hw->index));
++
++      /* init qmem */
++      wed_set(dev, MTK_WED_AMSDU_PSE, MTK_WED_AMSDU_PSE_RESET);
++      ret = mtk_wed_poll_busy(dev, MTK_WED_MON_AMSDU_QMEM_STS1, BIT(29));
++      if (ret) {
++              pr_info("%s: amsdu qmem initialization failed\n", __func__);
++              return ret;
++      }
++
++      /* eagle E1 PCIE1 tx ring 22 flow control issue */
++      if (dev->wlan.id == 0x7991)
++              wed_clr(dev, MTK_WED_AMSDU_FIFO, MTK_WED_AMSDU_IS_PRIOR0_RING);
++
++      wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN);
++
++      return 0;
++}
++
++static int
+ mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
+ {
+       u32 desc_size = dev->hw->soc->tx_ring_desc_size;
+@@ -708,6 +839,7 @@ __mtk_wed_detach(struct mtk_wed_device *
+       mtk_wdma_rx_reset(dev);
+       mtk_wed_reset(dev, MTK_WED_RESET_WED);
++      mtk_wed_amsdu_free_buffer(dev);
+       mtk_wed_free_tx_buffer(dev);
+       mtk_wed_free_tx_rings(dev);
+@@ -1128,23 +1260,6 @@ mtk_wed_ring_reset(struct mtk_wed_ring *
+       }
+ }
+-static u32
+-mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
+-{
+-      return !!(wed_r32(dev, reg) & mask);
+-}
+-
+-static int
+-mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
+-{
+-      int sleep = 15000;
+-      int timeout = 100 * sleep;
+-      u32 val;
+-
+-      return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
+-                               timeout, false, dev, reg, mask);
+-}
+-
+ static int
+ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+ {
+@@ -1691,6 +1806,7 @@ mtk_wed_start(struct mtk_wed_device *dev
+       }
+       mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
++      mtk_wed_amsdu_init(dev);
+       mtk_wed_dma_enable(dev);
+       dev->running = true;
+@@ -1747,6 +1863,10 @@ mtk_wed_attach(struct mtk_wed_device *de
+       if (ret)
+               goto out;
++      ret = mtk_wed_amsdu_buffer_alloc(dev);
++      if (ret)
++              goto out;
++
+       if (mtk_wed_get_rx_capa(dev)) {
+               ret = mtk_wed_rro_alloc(dev);
+               if (ret)
+--- a/drivers/net/ethernet/mediatek/mtk_wed.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
+@@ -25,6 +25,11 @@ struct mtk_wed_soc_data {
+       u32 wdma_desc_size;
+ };
++struct mtk_wed_amsdu {
++      void *txd;
++      dma_addr_t txd_phy;
++};
++
+ struct mtk_wed_hw {
+       const struct mtk_wed_soc_data *soc;
+       struct device_node *node;
+@@ -38,6 +43,7 @@ struct mtk_wed_hw {
+       struct dentry *debugfs_dir;
+       struct mtk_wed_device *wed_dev;
+       struct mtk_wed_wo *wed_wo;
++      struct mtk_wed_amsdu *wed_amsdu;
+       u32 pcie_base;
+       u32 debugfs_reg;
+       u32 num_flows;
+@@ -52,6 +58,7 @@ struct mtk_wdma_info {
+       u8 queue;
+       u16 wcid;
+       u8 bss;
++      u8 amsdu;
+ };
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -672,6 +672,82 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WOCPU_VIEW_MIOD_BASE                  0x8000
+ #define MTK_WED_PCIE_INT_MASK                         0x0
++#define MTK_WED_AMSDU_FIFO                            0x1800
++#define MTK_WED_AMSDU_IS_PRIOR0_RING                  BIT(10)
++
++#define MTK_WED_AMSDU_STA_INFO                                0x01810
++#define MTK_WED_AMSDU_STA_INFO_DO_INIT                        BIT(0)
++#define MTK_WED_AMSDU_STA_INFO_SET_INIT                       BIT(1)
++
++#define MTK_WED_AMSDU_STA_INFO_INIT                   0x01814
++#define MTK_WED_AMSDU_STA_WTBL_HDRT_MODE              BIT(0)
++#define MTK_WED_AMSDU_STA_RMVL                                BIT(1)
++#define MTK_WED_AMSDU_STA_MAX_AMSDU_LEN                       GENMASK(7, 2)
++#define MTK_WED_AMSDU_STA_MAX_AMSDU_NUM                       GENMASK(11, 8)
++
++#define MTK_WED_AMSDU_HIFTXD_BASE_L(_n)                       (0x1980 + (_n) * 0x4)
++
++#define MTK_WED_AMSDU_PSE                             0x1910
++#define MTK_WED_AMSDU_PSE_RESET                               BIT(16)
++
++#define MTK_WED_AMSDU_HIFTXD_CFG                      0x1968
++#define MTK_WED_AMSDU_HIFTXD_SRC                      GENMASK(16, 15)
++
++#define MTK_WED_MON_AMSDU_FIFO_DMAD                   0x1a34
++
++#define MTK_WED_MON_AMSDU_ENG_DMAD(_n)                        (0x1a80 + (_n) * 0x50)
++#define MTK_WED_MON_AMSDU_ENG_QFPL(_n)                        (0x1a84 + (_n) * 0x50)
++#define MTK_WED_MON_AMSDU_ENG_QENI(_n)                        (0x1a88 + (_n) * 0x50)
++#define MTK_WED_MON_AMSDU_ENG_QENO(_n)                        (0x1a8c + (_n) * 0x50)
++#define MTK_WED_MON_AMSDU_ENG_MERG(_n)                        (0x1a90 + (_n) * 0x50)
++
++#define MTK_WED_MON_AMSDU_ENG_CNT8(_n)                        (0x1a94 + (_n) * 0x50)
++#define MTK_WED_AMSDU_ENG_MAX_QGPP_CNT                        GENMASK(10, 0)
++#define MTK_WED_AMSDU_ENG_MAX_PL_CNT                  GENMASK(27, 16)
++
++#define MTK_WED_MON_AMSDU_ENG_CNT9(_n)                        (0x1a98 + (_n) * 0x50)
++#define MTK_WED_AMSDU_ENG_CUR_ENTRY                   GENMASK(10, 0)
++#define MTK_WED_AMSDU_ENG_MAX_BUF_MERGED              GENMASK(20, 16)
++#define MTK_WED_AMSDU_ENG_MAX_MSDU_MERGED             GENMASK(28, 24)
++
++#define MTK_WED_MON_AMSDU_QMEM_STS1                   0x1e04
++
++#define MTK_WED_MON_AMSDU_QMEM_CNT(_n)                        (0x1e0c + (_n) * 0x4)
++#define MTK_WED_AMSDU_QMEM_FQ_CNT                     GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_SP_QCNT                    GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID0_QCNT                  GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID1_QCNT                  GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID2_QCNT                  GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID3_QCNT                  GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID4_QCNT                  GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID5_QCNT                  GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID6_QCNT                  GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID7_QCNT                  GENMASK(11, 0)
++
++#define MTK_WED_MON_AMSDU_QMEM_PTR(_n)                        (0x1e20 + (_n) * 0x4)
++#define MTK_WED_AMSDU_QMEM_FQ_HEAD                    GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_SP_QHEAD                   GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID0_QHEAD                 GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID1_QHEAD                 GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID2_QHEAD                 GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID3_QHEAD                 GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID4_QHEAD                 GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID5_QHEAD                 GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID6_QHEAD                 GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID7_QHEAD                 GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_FQ_TAIL                    GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_SP_QTAIL                   GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID0_QTAIL                 GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID1_QTAIL                 GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID2_QTAIL                 GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID3_QTAIL                 GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID4_QTAIL                 GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID5_QTAIL                 GENMASK(11, 0)
++#define MTK_WED_AMSDU_QMEM_TID6_QTAIL                 GENMASK(27, 16)
++#define MTK_WED_AMSDU_QMEM_TID7_QTAIL                 GENMASK(11, 0)
++
++#define MTK_WED_MON_AMSDU_HIFTXD_FETCH_MSDU(_n)               (0x1ec4 + (_n) * 0x4)
++
+ #define MTK_WED_PCIE_BASE                     0x11280000
+ #define MTK_WED_PCIE_BASE0                    0x11300000
+ #define MTK_WED_PCIE_BASE1                    0x11310000
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -917,6 +917,7 @@ struct net_device_path {
+                       u8 queue;
+                       u16 wcid;
+                       u8 bss;
++                      u8 amsdu;
+               } mtk_wdma;
+       };
+ };
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -128,6 +128,7 @@ struct mtk_wed_device {
+               enum mtk_wed_bus_tye bus_type;
+               void __iomem *base;
+               u32 phy_base;
++              u32 id;
+               u32 wpdma_phys;
+               u32 wpdma_int;
+@@ -146,10 +147,12 @@ struct mtk_wed_device {
+               unsigned int rx_nbuf;
+               unsigned int rx_npkt;
+               unsigned int rx_size;
++              unsigned int amsdu_max_len;
+               u8 tx_tbit[MTK_WED_TX_QUEUES];
+               u8 rx_tbit[MTK_WED_RX_QUEUES];
+               u8 txfree_tbit;
++              u8 amsdu_max_subframes;
+               u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id);
+               int (*offload_enable)(struct mtk_wed_device *wed);
+@@ -223,6 +226,15 @@ static inline bool mtk_wed_get_rx_capa(s
+ #else
+       return false;
+ #endif
++}
++
++static inline bool mtk_wed_is_amsdu_supported(struct mtk_wed_device *dev)
++{
++#ifdef CONFIG_NET_MEDIATEK_SOC_WED
++      return dev->version == 3;
++#else
++      return false;
++#endif
+ }
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
diff --git a/target/linux/generic/backport-6.6/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch b/target/linux/generic/backport-6.6/752-17-v6.7-net-ethernet-mtk_wed-introduce-hw_rro-support-for-MT.patch
new file mode 100644 (file)
index 0000000..20befcc
--- /dev/null
@@ -0,0 +1,483 @@
+From: Sujuan Chen <sujuan.chen@mediatek.com>
+Date: Mon, 18 Sep 2023 12:29:16 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: introduce hw_rro support for MT7988
+
+MT7988 SoC support 802.11 receive reordering offload in hw while
+MT7986 SoC implements it through the firmware running on the mcu.
+
+Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -27,7 +27,7 @@
+ #define MTK_WED_BUF_SIZE              2048
+ #define MTK_WED_PAGE_BUF_SIZE         128
+ #define MTK_WED_BUF_PER_PAGE          (PAGE_SIZE / 2048)
+-#define MTK_WED_RX_PAGE_BUF_PER_PAGE  (PAGE_SIZE / 128)
++#define MTK_WED_RX_BUF_PER_PAGE               (PAGE_SIZE / MTK_WED_PAGE_BUF_SIZE)
+ #define MTK_WED_RX_RING_SIZE          1536
+ #define MTK_WED_RX_PG_BM_CNT          8192
+ #define MTK_WED_AMSDU_BUF_SIZE                (PAGE_SIZE << 4)
+@@ -597,6 +597,68 @@ free_pagelist:
+ }
+ static int
++mtk_wed_hwrro_buffer_alloc(struct mtk_wed_device *dev)
++{
++      int n_pages = MTK_WED_RX_PG_BM_CNT / MTK_WED_RX_BUF_PER_PAGE;
++      struct mtk_wed_buf *page_list;
++      struct mtk_wed_bm_desc *desc;
++      dma_addr_t desc_phys;
++      int i, page_idx = 0;
++
++      if (!dev->wlan.hw_rro)
++              return 0;
++
++      page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL);
++      if (!page_list)
++              return -ENOMEM;
++
++      dev->hw_rro.size = dev->wlan.rx_nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
++      dev->hw_rro.pages = page_list;
++      desc = dma_alloc_coherent(dev->hw->dev,
++                                dev->wlan.rx_nbuf * sizeof(*desc),
++                                &desc_phys, GFP_KERNEL);
++      if (!desc)
++              return -ENOMEM;
++
++      dev->hw_rro.desc = desc;
++      dev->hw_rro.desc_phys = desc_phys;
++
++      for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) {
++              dma_addr_t page_phys, buf_phys;
++              struct page *page;
++              int s;
++
++              page = __dev_alloc_page(GFP_KERNEL);
++              if (!page)
++                      return -ENOMEM;
++
++              page_phys = dma_map_page(dev->hw->dev, page, 0, PAGE_SIZE,
++                                       DMA_BIDIRECTIONAL);
++              if (dma_mapping_error(dev->hw->dev, page_phys)) {
++                      __free_page(page);
++                      return -ENOMEM;
++              }
++
++              page_list[page_idx].p = page;
++              page_list[page_idx++].phy_addr = page_phys;
++              dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE,
++                                      DMA_BIDIRECTIONAL);
++
++              buf_phys = page_phys;
++              for (s = 0; s < MTK_WED_RX_BUF_PER_PAGE; s++) {
++                      desc->buf0 = cpu_to_le32(buf_phys);
++                      buf_phys += MTK_WED_PAGE_BUF_SIZE;
++                      desc++;
++              }
++
++              dma_sync_single_for_device(dev->hw->dev, page_phys, PAGE_SIZE,
++                                         DMA_BIDIRECTIONAL);
++      }
++
++      return 0;
++}
++
++static int
+ mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev)
+ {
+       struct mtk_wed_bm_desc *desc;
+@@ -613,7 +675,42 @@ mtk_wed_rx_buffer_alloc(struct mtk_wed_d
+       dev->rx_buf_ring.desc_phys = desc_phys;
+       dev->wlan.init_rx_buf(dev, dev->wlan.rx_npkt);
+-      return 0;
++      return mtk_wed_hwrro_buffer_alloc(dev);
++}
++
++static void
++mtk_wed_hwrro_free_buffer(struct mtk_wed_device *dev)
++{
++      struct mtk_wed_buf *page_list = dev->hw_rro.pages;
++      struct mtk_wed_bm_desc *desc = dev->hw_rro.desc;
++      int i, page_idx = 0;
++
++      if (!dev->wlan.hw_rro)
++              return;
++
++      if (!page_list)
++              return;
++
++      if (!desc)
++              goto free_pagelist;
++
++      for (i = 0; i < MTK_WED_RX_PG_BM_CNT; i += MTK_WED_RX_BUF_PER_PAGE) {
++              dma_addr_t buf_addr = page_list[page_idx].phy_addr;
++              void *page = page_list[page_idx++].p;
++
++              if (!page)
++                      break;
++
++              dma_unmap_page(dev->hw->dev, buf_addr, PAGE_SIZE,
++                             DMA_BIDIRECTIONAL);
++              __free_page(page);
++      }
++
++      dma_free_coherent(dev->hw->dev, dev->hw_rro.size * sizeof(*desc),
++                        desc, dev->hw_rro.desc_phys);
++
++free_pagelist:
++      kfree(page_list);
+ }
+ static void
+@@ -627,6 +724,28 @@ mtk_wed_free_rx_buffer(struct mtk_wed_de
+       dev->wlan.release_rx_buf(dev);
+       dma_free_coherent(dev->hw->dev, dev->rx_buf_ring.size * sizeof(*desc),
+                         desc, dev->rx_buf_ring.desc_phys);
++
++      mtk_wed_hwrro_free_buffer(dev);
++}
++
++static void
++mtk_wed_hwrro_init(struct mtk_wed_device *dev)
++{
++      if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro)
++              return;
++
++      wed_set(dev, MTK_WED_RRO_PG_BM_RX_DMAM,
++              FIELD_PREP(MTK_WED_RRO_PG_BM_RX_SDL0, 128));
++
++      wed_w32(dev, MTK_WED_RRO_PG_BM_BASE, dev->hw_rro.desc_phys);
++
++      wed_w32(dev, MTK_WED_RRO_PG_BM_INIT_PTR,
++              MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX |
++              FIELD_PREP(MTK_WED_RRO_PG_BM_SW_TAIL_IDX,
++                         MTK_WED_RX_PG_BM_CNT));
++
++      /* enable rx_page_bm to fetch dmad */
++      wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN);
+ }
+ static void
+@@ -640,6 +759,8 @@ mtk_wed_rx_buffer_hw_init(struct mtk_wed
+       wed_w32(dev, MTK_WED_RX_BM_DYN_ALLOC_TH,
+               FIELD_PREP(MTK_WED_RX_BM_DYN_ALLOC_TH_H, 0xffff));
+       wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
++
++      mtk_wed_hwrro_init(dev);
+ }
+ static void
+@@ -934,6 +1055,8 @@ mtk_wed_bus_init(struct mtk_wed_device *
+ static void
+ mtk_wed_set_wpdma(struct mtk_wed_device *dev)
+ {
++      int i;
++
+       if (mtk_wed_is_v1(dev->hw)) {
+               wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_phys);
+               return;
+@@ -951,6 +1074,15 @@ mtk_wed_set_wpdma(struct mtk_wed_device
+       wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG, dev->wlan.wpdma_rx_glo);
+       wed_w32(dev, dev->hw->soc->regmap.wpdma_rx_ring0, dev->wlan.wpdma_rx);
++
++      if (!dev->wlan.hw_rro)
++              return;
++
++      wed_w32(dev, MTK_WED_RRO_RX_D_CFG(0), dev->wlan.wpdma_rx_rro[0]);
++      wed_w32(dev, MTK_WED_RRO_RX_D_CFG(1), dev->wlan.wpdma_rx_rro[1]);
++      for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++)
++              wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING_CFG(i),
++                      dev->wlan.wpdma_rx_pg + i * 0x10);
+ }
+ static void
+@@ -1762,6 +1894,165 @@ mtk_wed_dma_enable(struct mtk_wed_device
+ }
+ static void
++mtk_wed_start_hw_rro(struct mtk_wed_device *dev, u32 irq_mask, bool reset)
++{
++      int i;
++
++      wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask);
++      wed_w32(dev, MTK_WED_INT_MASK, irq_mask);
++
++      if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro)
++              return;
++
++      wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR);
++      wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
++              MTK_WED_RRO_MSDU_PG_DRV_CLR);
++
++      wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_RX,
++              MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN |
++              MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR |
++              MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN |
++              MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR |
++              FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG,
++                         dev->wlan.rro_rx_tbit[0]) |
++              FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG,
++                         dev->wlan.rro_rx_tbit[1]));
++
++      wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG,
++              MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN |
++              MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR |
++              MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN |
++              MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR |
++              MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN |
++              MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR |
++              FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG,
++                         dev->wlan.rx_pg_tbit[0]) |
++              FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG,
++                         dev->wlan.rx_pg_tbit[1]) |
++              FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG,
++                         dev->wlan.rx_pg_tbit[2]));
++
++      /* RRO_MSDU_PG_RING2_CFG1_FLD_DRV_EN should be enabled after
++       * WM FWDL completed, otherwise RRO_MSDU_PG ring may broken
++       */
++      wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
++              MTK_WED_RRO_MSDU_PG_DRV_EN);
++
++      for (i = 0; i < MTK_WED_RX_QUEUES; i++) {
++              struct mtk_wed_ring *ring = &dev->rx_rro_ring[i];
++
++              if (!(ring->flags & MTK_WED_RING_CONFIGURED))
++                      continue;
++
++              if (mtk_wed_check_wfdma_rx_fill(dev, ring))
++                      dev_err(dev->hw->dev,
++                              "rx_rro_ring(%d) initialization failed\n", i);
++      }
++
++      for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) {
++              struct mtk_wed_ring *ring = &dev->rx_page_ring[i];
++
++              if (!(ring->flags & MTK_WED_RING_CONFIGURED))
++                      continue;
++
++              if (mtk_wed_check_wfdma_rx_fill(dev, ring))
++                      dev_err(dev->hw->dev,
++                              "rx_page_ring(%d) initialization failed\n", i);
++      }
++}
++
++static void
++mtk_wed_rro_rx_ring_setup(struct mtk_wed_device *dev, int idx,
++                        void __iomem *regs)
++{
++      struct mtk_wed_ring *ring = &dev->rx_rro_ring[idx];
++
++      ring->wpdma = regs;
++      wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_BASE,
++              readl(regs));
++      wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_COUNT,
++              readl(regs + MTK_WED_RING_OFS_COUNT));
++      ring->flags |= MTK_WED_RING_CONFIGURED;
++}
++
++static void
++mtk_wed_msdu_pg_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
++{
++      struct mtk_wed_ring *ring = &dev->rx_page_ring[idx];
++
++      ring->wpdma = regs;
++      wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_BASE,
++              readl(regs));
++      wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_COUNT,
++              readl(regs + MTK_WED_RING_OFS_COUNT));
++      ring->flags |= MTK_WED_RING_CONFIGURED;
++}
++
++static int
++mtk_wed_ind_rx_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
++{
++      struct mtk_wed_ring *ring = &dev->ind_cmd_ring;
++      u32 val = readl(regs + MTK_WED_RING_OFS_COUNT);
++      int i, count = 0;
++
++      ring->wpdma = regs;
++      wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_BASE,
++              readl(regs) & 0xfffffff0);
++
++      wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_COUNT,
++              readl(regs + MTK_WED_RING_OFS_COUNT));
++
++      /* ack sn cr */
++      wed_w32(dev, MTK_WED_RRO_CFG0, dev->wlan.phy_base +
++              dev->wlan.ind_cmd.ack_sn_addr);
++      wed_w32(dev, MTK_WED_RRO_CFG1,
++              FIELD_PREP(MTK_WED_RRO_CFG1_MAX_WIN_SZ,
++                         dev->wlan.ind_cmd.win_size) |
++              FIELD_PREP(MTK_WED_RRO_CFG1_PARTICL_SE_ID,
++                         dev->wlan.ind_cmd.particular_sid));
++
++      /* particular session addr element */
++      wed_w32(dev, MTK_WED_ADDR_ELEM_CFG0,
++              dev->wlan.ind_cmd.particular_se_phys);
++
++      for (i = 0; i < dev->wlan.ind_cmd.se_group_nums; i++) {
++              wed_w32(dev, MTK_WED_RADDR_ELEM_TBL_WDATA,
++                      dev->wlan.ind_cmd.addr_elem_phys[i] >> 4);
++              wed_w32(dev, MTK_WED_ADDR_ELEM_TBL_CFG,
++                      MTK_WED_ADDR_ELEM_TBL_WR | (i & 0x7f));
++
++              val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG);
++              while (!(val & MTK_WED_ADDR_ELEM_TBL_WR_RDY) && count++ < 100)
++                      val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG);
++              if (count >= 100)
++                      dev_err(dev->hw->dev,
++                              "write ba session base failed\n");
++      }
++
++      /* pn check init */
++      for (i = 0; i < dev->wlan.ind_cmd.particular_sid; i++) {
++              wed_w32(dev, MTK_WED_PN_CHECK_WDATA_M,
++                      MTK_WED_PN_CHECK_IS_FIRST);
++
++              wed_w32(dev, MTK_WED_PN_CHECK_CFG, MTK_WED_PN_CHECK_WR |
++                      FIELD_PREP(MTK_WED_PN_CHECK_SE_ID, i));
++
++              count = 0;
++              val = wed_r32(dev, MTK_WED_PN_CHECK_CFG);
++              while (!(val & MTK_WED_PN_CHECK_WR_RDY) && count++ < 100)
++                      val = wed_r32(dev, MTK_WED_PN_CHECK_CFG);
++              if (count >= 100)
++                      dev_err(dev->hw->dev,
++                              "session(%d) initialization failed\n", i);
++      }
++
++      wed_w32(dev, MTK_WED_RX_IND_CMD_CNT0, MTK_WED_RX_IND_CMD_DBG_CNT_EN);
++      wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN);
++
++      return 0;
++}
++
++static void
+ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+ {
+       int i;
+@@ -2214,6 +2505,10 @@ void mtk_wed_add_hw(struct device_node *
+               .detach = mtk_wed_detach,
+               .ppe_check = mtk_wed_ppe_check,
+               .setup_tc = mtk_wed_setup_tc,
++              .start_hw_rro = mtk_wed_start_hw_rro,
++              .rro_rx_ring_setup = mtk_wed_rro_rx_ring_setup,
++              .msdu_pg_rx_ring_setup = mtk_wed_msdu_pg_rx_ring_setup,
++              .ind_rx_ring_setup = mtk_wed_ind_rx_ring_setup,
+       };
+       struct device_node *eth_np = eth->dev->of_node;
+       struct platform_device *pdev;
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -10,6 +10,7 @@
+ #define MTK_WED_TX_QUEUES             2
+ #define MTK_WED_RX_QUEUES             2
++#define MTK_WED_RX_PAGE_QUEUES                3
+ #define WED_WO_STA_REC                        0x6
+@@ -99,6 +100,9 @@ struct mtk_wed_device {
+       struct mtk_wed_ring txfree_ring;
+       struct mtk_wed_ring tx_wdma[MTK_WED_TX_QUEUES];
+       struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES];
++      struct mtk_wed_ring rx_rro_ring[MTK_WED_RX_QUEUES];
++      struct mtk_wed_ring rx_page_ring[MTK_WED_RX_PAGE_QUEUES];
++      struct mtk_wed_ring ind_cmd_ring;
+       struct {
+               int size;
+@@ -119,6 +123,13 @@ struct mtk_wed_device {
+               dma_addr_t fdbk_phys;
+       } rro;
++      struct {
++              int size;
++              struct mtk_wed_buf *pages;
++              struct mtk_wed_bm_desc *desc;
++              dma_addr_t desc_phys;
++      } hw_rro;
++
+       /* filled by driver: */
+       struct {
+               union {
+@@ -137,6 +148,8 @@ struct mtk_wed_device {
+               u32 wpdma_txfree;
+               u32 wpdma_rx_glo;
+               u32 wpdma_rx;
++              u32 wpdma_rx_rro[MTK_WED_RX_QUEUES];
++              u32 wpdma_rx_pg;
+               bool wcid_512;
+               bool hw_rro;
+@@ -151,9 +164,20 @@ struct mtk_wed_device {
+               u8 tx_tbit[MTK_WED_TX_QUEUES];
+               u8 rx_tbit[MTK_WED_RX_QUEUES];
++              u8 rro_rx_tbit[MTK_WED_RX_QUEUES];
++              u8 rx_pg_tbit[MTK_WED_RX_PAGE_QUEUES];
+               u8 txfree_tbit;
+               u8 amsdu_max_subframes;
++              struct {
++                      u8 se_group_nums;
++                      u16 win_size;
++                      u16 particular_sid;
++                      u32 ack_sn_addr;
++                      dma_addr_t particular_se_phys;
++                      dma_addr_t addr_elem_phys[1024];
++              } ind_cmd;
++
+               u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id);
+               int (*offload_enable)(struct mtk_wed_device *wed);
+               void (*offload_disable)(struct mtk_wed_device *wed);
+@@ -192,6 +216,14 @@ struct mtk_wed_ops {
+       void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
+       int (*setup_tc)(struct mtk_wed_device *wed, struct net_device *dev,
+                       enum tc_setup_type type, void *type_data);
++      void (*start_hw_rro)(struct mtk_wed_device *dev, u32 irq_mask,
++                           bool reset);
++      void (*rro_rx_ring_setup)(struct mtk_wed_device *dev, int ring,
++                                void __iomem *regs);
++      void (*msdu_pg_rx_ring_setup)(struct mtk_wed_device *dev, int ring,
++                                    void __iomem *regs);
++      int (*ind_rx_ring_setup)(struct mtk_wed_device *dev,
++                               void __iomem *regs);
+ };
+ extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
+@@ -263,6 +295,15 @@ static inline bool mtk_wed_is_amsdu_supp
+ #define mtk_wed_device_dma_reset(_dev) (_dev)->ops->reset_dma(_dev)
+ #define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) \
+       (_dev)->ops->setup_tc(_dev, _netdev, _type, _type_data)
++#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) \
++      (_dev)->ops->start_hw_rro(_dev, _mask, _reset)
++#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) \
++      (_dev)->ops->rro_rx_ring_setup(_dev, _ring, _regs)
++#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) \
++      (_dev)->ops->msdu_pg_rx_ring_setup(_dev, _ring, _regs)
++#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) \
++      (_dev)->ops->ind_rx_ring_setup(_dev, _regs)
++
+ #else
+ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
+ {
+@@ -282,6 +323,10 @@ static inline bool mtk_wed_device_active
+ #define mtk_wed_device_stop(_dev) do {} while (0)
+ #define mtk_wed_device_dma_reset(_dev) do {} while (0)
+ #define mtk_wed_device_setup_tc(_dev, _netdev, _type, _type_data) -EOPNOTSUPP
++#define mtk_wed_device_start_hw_rro(_dev, _mask, _reset) do {} while (0)
++#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) -ENODEV
++#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) -ENODEV
++#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) -ENODEV
+ #endif
+ #endif
diff --git a/target/linux/generic/backport-6.6/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch b/target/linux/generic/backport-6.6/752-18-v6.7-net-ethernet-mtk_wed-debugfs-move-wed_v2-specific-re.patch
new file mode 100644 (file)
index 0000000..5ea43a4
--- /dev/null
@@ -0,0 +1,78 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 18 Sep 2023 12:29:17 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: debugfs: move wed_v2 specific regs
+ out of regs array
+
+Move specific WED2.0 debugfs entries out of regs array. This is a
+preliminary patch to introduce WED 3.0 debugfs info.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
+@@ -151,7 +151,7 @@ DEFINE_SHOW_ATTRIBUTE(wed_txinfo);
+ static int
+ wed_rxinfo_show(struct seq_file *s, void *data)
+ {
+-      static const struct reg_dump regs[] = {
++      static const struct reg_dump regs_common[] = {
+               DUMP_STR("WPDMA RX"),
+               DUMP_WPDMA_RX_RING(0),
+               DUMP_WPDMA_RX_RING(1),
+@@ -169,7 +169,7 @@ wed_rxinfo_show(struct seq_file *s, void
+               DUMP_WED_RING(WED_RING_RX_DATA(0)),
+               DUMP_WED_RING(WED_RING_RX_DATA(1)),
+-              DUMP_STR("WED RRO"),
++              DUMP_STR("WED WO RRO"),
+               DUMP_WED_RRO_RING(WED_RROQM_MIOD_CTRL0),
+               DUMP_WED(WED_RROQM_MID_MIB),
+               DUMP_WED(WED_RROQM_MOD_MIB),
+@@ -180,17 +180,6 @@ wed_rxinfo_show(struct seq_file *s, void
+               DUMP_WED(WED_RROQM_FDBK_ANC_MIB),
+               DUMP_WED(WED_RROQM_FDBK_ANC2H_MIB),
+-              DUMP_STR("WED Route QM"),
+-              DUMP_WED(WED_RTQM_R2H_MIB(0)),
+-              DUMP_WED(WED_RTQM_R2Q_MIB(0)),
+-              DUMP_WED(WED_RTQM_Q2H_MIB(0)),
+-              DUMP_WED(WED_RTQM_R2H_MIB(1)),
+-              DUMP_WED(WED_RTQM_R2Q_MIB(1)),
+-              DUMP_WED(WED_RTQM_Q2H_MIB(1)),
+-              DUMP_WED(WED_RTQM_Q2N_MIB),
+-              DUMP_WED(WED_RTQM_Q2B_MIB),
+-              DUMP_WED(WED_RTQM_PFDBK_MIB),
+-
+               DUMP_STR("WED WDMA TX"),
+               DUMP_WED(WED_WDMA_TX_MIB),
+               DUMP_WED_RING(WED_WDMA_RING_TX),
+@@ -211,11 +200,25 @@ wed_rxinfo_show(struct seq_file *s, void
+               DUMP_WED(WED_RX_BM_INTF),
+               DUMP_WED(WED_RX_BM_ERR_STS),
+       };
++      static const struct reg_dump regs_wed_v2[] = {
++              DUMP_STR("WED Route QM"),
++              DUMP_WED(WED_RTQM_R2H_MIB(0)),
++              DUMP_WED(WED_RTQM_R2Q_MIB(0)),
++              DUMP_WED(WED_RTQM_Q2H_MIB(0)),
++              DUMP_WED(WED_RTQM_R2H_MIB(1)),
++              DUMP_WED(WED_RTQM_R2Q_MIB(1)),
++              DUMP_WED(WED_RTQM_Q2H_MIB(1)),
++              DUMP_WED(WED_RTQM_Q2N_MIB),
++              DUMP_WED(WED_RTQM_Q2B_MIB),
++              DUMP_WED(WED_RTQM_PFDBK_MIB),
++      };
+       struct mtk_wed_hw *hw = s->private;
+       struct mtk_wed_device *dev = hw->wed_dev;
+-      if (dev)
+-              dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
++      if (dev) {
++              dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common));
++              dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2));
++      }
+       return 0;
+ }
diff --git a/target/linux/generic/backport-6.6/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch b/target/linux/generic/backport-6.6/752-19-v6.7-net-ethernet-mtk_wed-debugfs-add-WED-3.0-debugfs-ent.patch
new file mode 100644 (file)
index 0000000..9730c30
--- /dev/null
@@ -0,0 +1,432 @@
+From: Sujuan Chen <sujuan.chen@mediatek.com>
+Date: Mon, 18 Sep 2023 12:29:18 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: debugfs: add WED 3.0 debugfs entries
+
+Introduce WED3.0 debugfs entries useful for debugging.
+
+Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
+@@ -11,6 +11,7 @@ struct reg_dump {
+       u16 offset;
+       u8 type;
+       u8 base;
++      u32 mask;
+ };
+ enum {
+@@ -25,6 +26,8 @@ enum {
+ #define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING }
+ #define DUMP_REG(_reg, ...) { #_reg, MTK_##_reg, __VA_ARGS__ }
++#define DUMP_REG_MASK(_reg, _mask)    \
++      { #_mask, MTK_##_reg, DUMP_TYPE_WED, 0, MTK_##_mask }
+ #define DUMP_RING(_prefix, _base, ...)                                \
+       { _prefix " BASE", _base, __VA_ARGS__ },                \
+       { _prefix " CNT",  _base + 0x4, __VA_ARGS__ },  \
+@@ -32,6 +35,7 @@ enum {
+       { _prefix " DIDX", _base + 0xc, __VA_ARGS__ }
+ #define DUMP_WED(_reg) DUMP_REG(_reg, DUMP_TYPE_WED)
++#define DUMP_WED_MASK(_reg, _mask) DUMP_REG_MASK(_reg, _mask)
+ #define DUMP_WED_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WED)
+ #define DUMP_WDMA(_reg) DUMP_REG(_reg, DUMP_TYPE_WDMA)
+@@ -212,12 +216,58 @@ wed_rxinfo_show(struct seq_file *s, void
+               DUMP_WED(WED_RTQM_Q2B_MIB),
+               DUMP_WED(WED_RTQM_PFDBK_MIB),
+       };
++      static const struct reg_dump regs_wed_v3[] = {
++              DUMP_STR("WED RX RRO DATA"),
++              DUMP_WED_RING(WED_RRO_RX_D_RX(0)),
++              DUMP_WED_RING(WED_RRO_RX_D_RX(1)),
++
++              DUMP_STR("WED RX MSDU PAGE"),
++              DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(0)),
++              DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(1)),
++              DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(2)),
++
++              DUMP_STR("WED RX IND CMD"),
++              DUMP_WED(WED_IND_CMD_RX_CTRL1),
++              DUMP_WED_MASK(WED_IND_CMD_RX_CTRL2, WED_IND_CMD_MAX_CNT),
++              DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_PROC_IDX),
++              DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_DMA_IDX),
++              DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_MAGIC_CNT),
++              DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_MAGIC_CNT),
++              DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0,
++                            WED_IND_CMD_PREFETCH_FREE_CNT),
++              DUMP_WED_MASK(WED_RRO_CFG1, WED_RRO_CFG1_PARTICL_SE_ID),
++
++              DUMP_STR("WED ADDR ELEM"),
++              DUMP_WED(WED_ADDR_ELEM_CFG0),
++              DUMP_WED_MASK(WED_ADDR_ELEM_CFG1,
++                            WED_ADDR_ELEM_PREFETCH_FREE_CNT),
++
++              DUMP_STR("WED Route QM"),
++              DUMP_WED(WED_RTQM_ENQ_I2Q_DMAD_CNT),
++              DUMP_WED(WED_RTQM_ENQ_I2N_DMAD_CNT),
++              DUMP_WED(WED_RTQM_ENQ_I2Q_PKT_CNT),
++              DUMP_WED(WED_RTQM_ENQ_I2N_PKT_CNT),
++              DUMP_WED(WED_RTQM_ENQ_USED_ENTRY_CNT),
++              DUMP_WED(WED_RTQM_ENQ_ERR_CNT),
++
++              DUMP_WED(WED_RTQM_DEQ_DMAD_CNT),
++              DUMP_WED(WED_RTQM_DEQ_Q2I_DMAD_CNT),
++              DUMP_WED(WED_RTQM_DEQ_PKT_CNT),
++              DUMP_WED(WED_RTQM_DEQ_Q2I_PKT_CNT),
++              DUMP_WED(WED_RTQM_DEQ_USED_PFDBK_CNT),
++              DUMP_WED(WED_RTQM_DEQ_ERR_CNT),
++      };
+       struct mtk_wed_hw *hw = s->private;
+       struct mtk_wed_device *dev = hw->wed_dev;
+       if (dev) {
+               dump_wed_regs(s, dev, regs_common, ARRAY_SIZE(regs_common));
+-              dump_wed_regs(s, dev, regs_wed_v2, ARRAY_SIZE(regs_wed_v2));
++              if (mtk_wed_is_v2(hw))
++                      dump_wed_regs(s, dev,
++                                    regs_wed_v2, ARRAY_SIZE(regs_wed_v2));
++              else
++                      dump_wed_regs(s, dev,
++                                    regs_wed_v3, ARRAY_SIZE(regs_wed_v3));
+       }
+       return 0;
+@@ -225,6 +275,314 @@ wed_rxinfo_show(struct seq_file *s, void
+ DEFINE_SHOW_ATTRIBUTE(wed_rxinfo);
+ static int
++wed_amsdu_show(struct seq_file *s, void *data)
++{
++      static const struct reg_dump regs[] = {
++              DUMP_STR("WED AMDSU INFO"),
++              DUMP_WED(WED_MON_AMSDU_FIFO_DMAD),
++
++              DUMP_STR("WED AMDSU ENG0 INFO"),
++              DUMP_WED(WED_MON_AMSDU_ENG_DMAD(0)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QFPL(0)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENI(0)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENO(0)),
++              DUMP_WED(WED_MON_AMSDU_ENG_MERG(0)),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0),
++                            WED_AMSDU_ENG_MAX_PL_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(0),
++                            WED_AMSDU_ENG_MAX_QGPP_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0),
++                            WED_AMSDU_ENG_CUR_ENTRY),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0),
++                            WED_AMSDU_ENG_MAX_BUF_MERGED),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(0),
++                            WED_AMSDU_ENG_MAX_MSDU_MERGED),
++
++              DUMP_STR("WED AMDSU ENG1 INFO"),
++              DUMP_WED(WED_MON_AMSDU_ENG_DMAD(1)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QFPL(1)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENI(1)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENO(1)),
++              DUMP_WED(WED_MON_AMSDU_ENG_MERG(1)),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1),
++                            WED_AMSDU_ENG_MAX_PL_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(1),
++                            WED_AMSDU_ENG_MAX_QGPP_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(1),
++                            WED_AMSDU_ENG_CUR_ENTRY),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
++                            WED_AMSDU_ENG_MAX_BUF_MERGED),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
++                            WED_AMSDU_ENG_MAX_MSDU_MERGED),
++
++              DUMP_STR("WED AMDSU ENG2 INFO"),
++              DUMP_WED(WED_MON_AMSDU_ENG_DMAD(2)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QFPL(2)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENI(2)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENO(2)),
++              DUMP_WED(WED_MON_AMSDU_ENG_MERG(2)),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2),
++                            WED_AMSDU_ENG_MAX_PL_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(2),
++                            WED_AMSDU_ENG_MAX_QGPP_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
++                            WED_AMSDU_ENG_CUR_ENTRY),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
++                            WED_AMSDU_ENG_MAX_BUF_MERGED),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(2),
++                            WED_AMSDU_ENG_MAX_MSDU_MERGED),
++
++              DUMP_STR("WED AMDSU ENG3 INFO"),
++              DUMP_WED(WED_MON_AMSDU_ENG_DMAD(3)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QFPL(3)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENI(3)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENO(3)),
++              DUMP_WED(WED_MON_AMSDU_ENG_MERG(3)),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3),
++                            WED_AMSDU_ENG_MAX_PL_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(3),
++                            WED_AMSDU_ENG_MAX_QGPP_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3),
++                            WED_AMSDU_ENG_CUR_ENTRY),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3),
++                            WED_AMSDU_ENG_MAX_BUF_MERGED),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(3),
++                            WED_AMSDU_ENG_MAX_MSDU_MERGED),
++
++              DUMP_STR("WED AMDSU ENG4 INFO"),
++              DUMP_WED(WED_MON_AMSDU_ENG_DMAD(4)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QFPL(4)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENI(4)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENO(4)),
++              DUMP_WED(WED_MON_AMSDU_ENG_MERG(4)),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4),
++                            WED_AMSDU_ENG_MAX_PL_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(4),
++                            WED_AMSDU_ENG_MAX_QGPP_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
++                            WED_AMSDU_ENG_CUR_ENTRY),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
++                            WED_AMSDU_ENG_MAX_BUF_MERGED),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
++                            WED_AMSDU_ENG_MAX_MSDU_MERGED),
++
++              DUMP_STR("WED AMDSU ENG5 INFO"),
++              DUMP_WED(WED_MON_AMSDU_ENG_DMAD(5)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QFPL(5)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENI(5)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENO(5)),
++              DUMP_WED(WED_MON_AMSDU_ENG_MERG(5)),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5),
++                            WED_AMSDU_ENG_MAX_PL_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(5),
++                            WED_AMSDU_ENG_MAX_QGPP_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5),
++                            WED_AMSDU_ENG_CUR_ENTRY),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5),
++                            WED_AMSDU_ENG_MAX_BUF_MERGED),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(5),
++                            WED_AMSDU_ENG_MAX_MSDU_MERGED),
++
++              DUMP_STR("WED AMDSU ENG6 INFO"),
++              DUMP_WED(WED_MON_AMSDU_ENG_DMAD(6)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QFPL(6)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENI(6)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENO(6)),
++              DUMP_WED(WED_MON_AMSDU_ENG_MERG(6)),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6),
++                            WED_AMSDU_ENG_MAX_PL_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(6),
++                            WED_AMSDU_ENG_MAX_QGPP_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6),
++                            WED_AMSDU_ENG_CUR_ENTRY),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6),
++                            WED_AMSDU_ENG_MAX_BUF_MERGED),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(6),
++                            WED_AMSDU_ENG_MAX_MSDU_MERGED),
++
++              DUMP_STR("WED AMDSU ENG7 INFO"),
++              DUMP_WED(WED_MON_AMSDU_ENG_DMAD(7)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QFPL(7)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENI(7)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENO(7)),
++              DUMP_WED(WED_MON_AMSDU_ENG_MERG(7)),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7),
++                            WED_AMSDU_ENG_MAX_PL_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(7),
++                            WED_AMSDU_ENG_MAX_QGPP_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7),
++                            WED_AMSDU_ENG_CUR_ENTRY),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(7),
++                            WED_AMSDU_ENG_MAX_BUF_MERGED),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(4),
++                            WED_AMSDU_ENG_MAX_MSDU_MERGED),
++
++              DUMP_STR("WED AMDSU ENG8 INFO"),
++              DUMP_WED(WED_MON_AMSDU_ENG_DMAD(8)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QFPL(8)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENI(8)),
++              DUMP_WED(WED_MON_AMSDU_ENG_QENO(8)),
++              DUMP_WED(WED_MON_AMSDU_ENG_MERG(8)),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8),
++                            WED_AMSDU_ENG_MAX_PL_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT8(8),
++                            WED_AMSDU_ENG_MAX_QGPP_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8),
++                            WED_AMSDU_ENG_CUR_ENTRY),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8),
++                            WED_AMSDU_ENG_MAX_BUF_MERGED),
++              DUMP_WED_MASK(WED_MON_AMSDU_ENG_CNT9(8),
++                            WED_AMSDU_ENG_MAX_MSDU_MERGED),
++
++              DUMP_STR("WED QMEM INFO"),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_FQ_CNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(0), WED_AMSDU_QMEM_SP_QCNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID0_QCNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(1), WED_AMSDU_QMEM_TID1_QCNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID2_QCNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(2), WED_AMSDU_QMEM_TID3_QCNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID4_QCNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(3), WED_AMSDU_QMEM_TID5_QCNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID6_QCNT),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_CNT(4), WED_AMSDU_QMEM_TID7_QCNT),
++
++              DUMP_STR("WED QMEM HEAD INFO"),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_FQ_HEAD),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(0), WED_AMSDU_QMEM_SP_QHEAD),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID0_QHEAD),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(1), WED_AMSDU_QMEM_TID1_QHEAD),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID2_QHEAD),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(2), WED_AMSDU_QMEM_TID3_QHEAD),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID4_QHEAD),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(3), WED_AMSDU_QMEM_TID5_QHEAD),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID6_QHEAD),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(4), WED_AMSDU_QMEM_TID7_QHEAD),
++
++              DUMP_STR("WED QMEM TAIL INFO"),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_FQ_TAIL),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(5), WED_AMSDU_QMEM_SP_QTAIL),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID0_QTAIL),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(6), WED_AMSDU_QMEM_TID1_QTAIL),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID2_QTAIL),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(7), WED_AMSDU_QMEM_TID3_QTAIL),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID4_QTAIL),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(8), WED_AMSDU_QMEM_TID5_QTAIL),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID6_QTAIL),
++              DUMP_WED_MASK(WED_MON_AMSDU_QMEM_PTR(9), WED_AMSDU_QMEM_TID7_QTAIL),
++
++              DUMP_STR("WED HIFTXD MSDU INFO"),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(1)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(2)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(3)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(4)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(5)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(6)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(7)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(8)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(9)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(10)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(11)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(12)),
++              DUMP_WED(WED_MON_AMSDU_HIFTXD_FETCH_MSDU(13)),
++      };
++      struct mtk_wed_hw *hw = s->private;
++      struct mtk_wed_device *dev = hw->wed_dev;
++
++      if (dev)
++              dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
++
++      return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(wed_amsdu);
++
++static int
++wed_rtqm_show(struct seq_file *s, void *data)
++{
++      static const struct reg_dump regs[] = {
++              DUMP_STR("WED Route QM IGRS0(N2H + Recycle)"),
++              DUMP_WED(WED_RTQM_IGRS0_I2HW_DMAD_CNT),
++              DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(0)),
++              DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(1)),
++              DUMP_WED(WED_RTQM_IGRS0_I2HW_PKT_CNT),
++              DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)),
++              DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)),
++              DUMP_WED(WED_RTQM_IGRS0_FDROP_CNT),
++
++              DUMP_STR("WED Route QM IGRS1(Legacy)"),
++              DUMP_WED(WED_RTQM_IGRS1_I2HW_DMAD_CNT),
++              DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(0)),
++              DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(1)),
++              DUMP_WED(WED_RTQM_IGRS1_I2HW_PKT_CNT),
++              DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(0)),
++              DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(1)),
++              DUMP_WED(WED_RTQM_IGRS1_FDROP_CNT),
++
++              DUMP_STR("WED Route QM IGRS2(RRO3.0)"),
++              DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT),
++              DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(0)),
++              DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(1)),
++              DUMP_WED(WED_RTQM_IGRS2_I2HW_PKT_CNT),
++              DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(0)),
++              DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(1)),
++              DUMP_WED(WED_RTQM_IGRS2_FDROP_CNT),
++
++              DUMP_STR("WED Route QM IGRS3(DEBUG)"),
++              DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT),
++              DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(0)),
++              DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(1)),
++              DUMP_WED(WED_RTQM_IGRS3_I2HW_PKT_CNT),
++              DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(0)),
++              DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(1)),
++              DUMP_WED(WED_RTQM_IGRS3_FDROP_CNT),
++      };
++      struct mtk_wed_hw *hw = s->private;
++      struct mtk_wed_device *dev = hw->wed_dev;
++
++      if (dev)
++              dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
++
++      return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(wed_rtqm);
++
++static int
++wed_rro_show(struct seq_file *s, void *data)
++{
++      static const struct reg_dump regs[] = {
++              DUMP_STR("RRO/IND CMD CNT"),
++              DUMP_WED(WED_RX_IND_CMD_CNT(1)),
++              DUMP_WED(WED_RX_IND_CMD_CNT(2)),
++              DUMP_WED(WED_RX_IND_CMD_CNT(3)),
++              DUMP_WED(WED_RX_IND_CMD_CNT(4)),
++              DUMP_WED(WED_RX_IND_CMD_CNT(5)),
++              DUMP_WED(WED_RX_IND_CMD_CNT(6)),
++              DUMP_WED(WED_RX_IND_CMD_CNT(7)),
++              DUMP_WED(WED_RX_IND_CMD_CNT(8)),
++              DUMP_WED_MASK(WED_RX_IND_CMD_CNT(9),
++                            WED_IND_CMD_MAGIC_CNT_FAIL_CNT),
++
++              DUMP_WED(WED_RX_ADDR_ELEM_CNT(0)),
++              DUMP_WED_MASK(WED_RX_ADDR_ELEM_CNT(1),
++                            WED_ADDR_ELEM_SIG_FAIL_CNT),
++              DUMP_WED(WED_RX_MSDU_PG_CNT(1)),
++              DUMP_WED(WED_RX_MSDU_PG_CNT(2)),
++              DUMP_WED(WED_RX_MSDU_PG_CNT(3)),
++              DUMP_WED(WED_RX_MSDU_PG_CNT(4)),
++              DUMP_WED(WED_RX_MSDU_PG_CNT(5)),
++              DUMP_WED_MASK(WED_RX_PN_CHK_CNT,
++                            WED_PN_CHK_FAIL_CNT),
++      };
++      struct mtk_wed_hw *hw = s->private;
++      struct mtk_wed_device *dev = hw->wed_dev;
++
++      if (dev)
++              dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
++
++      return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(wed_rro);
++
++static int
+ mtk_wed_reg_set(void *data, u64 val)
+ {
+       struct mtk_wed_hw *hw = data;
+@@ -264,7 +622,16 @@ void mtk_wed_hw_add_debugfs(struct mtk_w
+       debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg);
+       debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval);
+       debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops);
+-      if (!mtk_wed_is_v1(hw))
++      if (!mtk_wed_is_v1(hw)) {
+               debugfs_create_file_unsafe("rxinfo", 0400, dir, hw,
+                                          &wed_rxinfo_fops);
++              if (mtk_wed_is_v3_or_greater(hw)) {
++                      debugfs_create_file_unsafe("amsdu", 0400, dir, hw,
++                                                 &wed_amsdu_fops);
++                      debugfs_create_file_unsafe("rtqm", 0400, dir, hw,
++                                                 &wed_rtqm_fops);
++                      debugfs_create_file_unsafe("rro", 0400, dir, hw,
++                                                 &wed_rro_fops);
++              }
++      }
+ }
diff --git a/target/linux/generic/backport-6.6/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch b/target/linux/generic/backport-6.6/752-20-v6.7-net-ethernet-mtk_wed-add-wed-3.0-reset-support.patch
new file mode 100644 (file)
index 0000000..7dad210
--- /dev/null
@@ -0,0 +1,587 @@
+From: Sujuan Chen <sujuan.chen@mediatek.com>
+Date: Mon, 18 Sep 2023 12:29:19 +0200
+Subject: [PATCH] net: ethernet: mtk_wed: add wed 3.0 reset support
+
+Introduce support for resetting Wireless Ethernet Dispatcher 3.0
+available on MT988 SoC.
+
+Co-developed-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -149,6 +149,90 @@ mtk_wdma_read_reset(struct mtk_wed_devic
+       return wdma_r32(dev, MTK_WDMA_GLO_CFG);
+ }
++static void
++mtk_wdma_v3_rx_reset(struct mtk_wed_device *dev)
++{
++      u32 status;
++
++      if (!mtk_wed_is_v3_or_greater(dev->hw))
++              return;
++
++      wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
++      wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
++
++      if (read_poll_timeout(wdma_r32, status,
++                            !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY),
++                            0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG))
++              dev_err(dev->hw->dev, "rx reset failed\n");
++
++      if (read_poll_timeout(wdma_r32, status,
++                            !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY),
++                            0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG))
++              dev_err(dev->hw->dev, "rx reset failed\n");
++
++      wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
++      wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
++
++      if (read_poll_timeout(wdma_r32, status,
++                            !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY),
++                            0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG))
++              dev_err(dev->hw->dev, "rx reset failed\n");
++
++      if (read_poll_timeout(wdma_r32, status,
++                            !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY),
++                            0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG))
++              dev_err(dev->hw->dev, "rx reset failed\n");
++
++      /* prefetch FIFO */
++      wdma_w32(dev, MTK_WDMA_PREF_RX_FIFO_CFG,
++               MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR |
++               MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR);
++      wdma_clr(dev, MTK_WDMA_PREF_RX_FIFO_CFG,
++               MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR |
++               MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR);
++
++      /* core FIFO */
++      wdma_w32(dev, MTK_WDMA_XDMA_RX_FIFO_CFG,
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR);
++      wdma_clr(dev, MTK_WDMA_XDMA_RX_FIFO_CFG,
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR |
++               MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR);
++
++      /* writeback FIFO */
++      wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0),
++               MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
++      wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1),
++               MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
++
++      wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0),
++               MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
++      wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1),
++               MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
++
++      /* prefetch ring status */
++      wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG,
++               MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR);
++      wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG,
++               MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR);
++
++      /* writeback ring status */
++      wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG,
++               MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR);
++      wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG,
++               MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR);
++}
++
+ static int
+ mtk_wdma_rx_reset(struct mtk_wed_device *dev)
+ {
+@@ -161,6 +245,7 @@ mtk_wdma_rx_reset(struct mtk_wed_device
+       if (ret)
+               dev_err(dev->hw->dev, "rx reset failed\n");
++      mtk_wdma_v3_rx_reset(dev);
+       wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
+       wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
+@@ -193,6 +278,84 @@ mtk_wed_poll_busy(struct mtk_wed_device
+ }
+ static void
++mtk_wdma_v3_tx_reset(struct mtk_wed_device *dev)
++{
++      u32 status;
++
++      if (!mtk_wed_is_v3_or_greater(dev->hw))
++              return;
++
++      wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
++      wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
++
++      if (read_poll_timeout(wdma_r32, status,
++                            !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY),
++                            0, 10000, false, dev, MTK_WDMA_PREF_TX_CFG))
++              dev_err(dev->hw->dev, "tx reset failed\n");
++
++      if (read_poll_timeout(wdma_r32, status,
++                            !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY),
++                            0, 10000, false, dev, MTK_WDMA_PREF_RX_CFG))
++              dev_err(dev->hw->dev, "tx reset failed\n");
++
++      wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
++      wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
++
++      if (read_poll_timeout(wdma_r32, status,
++                            !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY),
++                            0, 10000, false, dev, MTK_WDMA_WRBK_TX_CFG))
++              dev_err(dev->hw->dev, "tx reset failed\n");
++
++      if (read_poll_timeout(wdma_r32, status,
++                            !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY),
++                            0, 10000, false, dev, MTK_WDMA_WRBK_RX_CFG))
++              dev_err(dev->hw->dev, "tx reset failed\n");
++
++      /* prefetch FIFO */
++      wdma_w32(dev, MTK_WDMA_PREF_TX_FIFO_CFG,
++               MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR |
++               MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR);
++      wdma_clr(dev, MTK_WDMA_PREF_TX_FIFO_CFG,
++               MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR |
++               MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR);
++
++      /* core FIFO */
++      wdma_w32(dev, MTK_WDMA_XDMA_TX_FIFO_CFG,
++               MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR |
++               MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR |
++               MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR |
++               MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR);
++      wdma_clr(dev, MTK_WDMA_XDMA_TX_FIFO_CFG,
++               MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR |
++               MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR |
++               MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR |
++               MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR);
++
++      /* writeback FIFO */
++      wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0),
++               MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
++      wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1),
++               MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
++
++      wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0),
++               MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
++      wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1),
++               MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
++
++      /* prefetch ring status */
++      wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG,
++               MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR);
++      wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG,
++               MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR);
++
++      /* writeback ring status */
++      wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG,
++               MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR);
++      wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG,
++               MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR);
++}
++
++static void
+ mtk_wdma_tx_reset(struct mtk_wed_device *dev)
+ {
+       u32 status, mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY;
+@@ -203,6 +366,7 @@ mtk_wdma_tx_reset(struct mtk_wed_device
+                              !(status & mask), 0, 10000))
+               dev_err(dev->hw->dev, "tx reset failed\n");
++      mtk_wdma_v3_tx_reset(dev);
+       wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX);
+       wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
+@@ -1405,13 +1569,33 @@ mtk_wed_rx_reset(struct mtk_wed_device *
+       if (ret)
+               return ret;
++      if (dev->wlan.hw_rro) {
++              wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN);
++              mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_HW_STS,
++                                MTK_WED_RX_IND_CMD_BUSY);
++              mtk_wed_reset(dev, MTK_WED_RESET_RRO_RX_TO_PG);
++      }
++
+       wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN);
+       ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
+                               MTK_WED_WPDMA_RX_D_RX_DRV_BUSY);
++      if (!ret && mtk_wed_is_v3_or_greater(dev->hw))
++              ret = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
++                                      MTK_WED_WPDMA_RX_D_PREF_BUSY);
+       if (ret) {
+               mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
+               mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV);
+       } else {
++              if (mtk_wed_is_v3_or_greater(dev->hw)) {
++                      /* 1.a. disable prefetch HW */
++                      wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
++                              MTK_WED_WPDMA_RX_D_PREF_EN);
++                      mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
++                                        MTK_WED_WPDMA_RX_D_PREF_BUSY);
++                      wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
++                              MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL);
++              }
++
+               wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
+                       MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
+                       MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
+@@ -1439,23 +1623,52 @@ mtk_wed_rx_reset(struct mtk_wed_device *
+               wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0);
+       }
++      if (dev->wlan.hw_rro) {
++              /* disable rro msdu page drv */
++              wed_clr(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
++                      MTK_WED_RRO_MSDU_PG_DRV_EN);
++
++              /* disable rro data drv */
++              wed_clr(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN);
++
++              /* rro msdu page drv reset */
++              wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
++                      MTK_WED_RRO_MSDU_PG_DRV_CLR);
++              mtk_wed_poll_busy(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
++                                MTK_WED_RRO_MSDU_PG_DRV_CLR);
++
++              /* rro data drv reset */
++              wed_w32(dev, MTK_WED_RRO_RX_D_CFG(2),
++                      MTK_WED_RRO_RX_D_DRV_CLR);
++              mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_D_CFG(2),
++                                MTK_WED_RRO_RX_D_DRV_CLR);
++      }
++
+       /* reset route qm */
+       wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
+       ret = mtk_wed_poll_busy(dev, MTK_WED_CTRL,
+                               MTK_WED_CTRL_RX_ROUTE_QM_BUSY);
+-      if (ret)
++      if (ret) {
+               mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM);
+-      else
+-              wed_set(dev, MTK_WED_RTQM_GLO_CFG,
+-                      MTK_WED_RTQM_Q_RST);
++      } else if (mtk_wed_is_v3_or_greater(dev->hw)) {
++              wed_set(dev, MTK_WED_RTQM_RST, BIT(0));
++              wed_clr(dev, MTK_WED_RTQM_RST, BIT(0));
++              mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM);
++      } else {
++              wed_set(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
++      }
+       /* reset tx wdma */
+       mtk_wdma_tx_reset(dev);
+       /* reset tx wdma drv */
+       wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN);
+-      mtk_wed_poll_busy(dev, MTK_WED_CTRL,
+-                        MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
++      if (mtk_wed_is_v3_or_greater(dev->hw))
++              mtk_wed_poll_busy(dev, MTK_WED_WPDMA_STATUS,
++                                MTK_WED_WPDMA_STATUS_TX_DRV);
++      else
++              mtk_wed_poll_busy(dev, MTK_WED_CTRL,
++                                MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
+       mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV);
+       /* reset wed rx dma */
+@@ -1476,6 +1689,14 @@ mtk_wed_rx_reset(struct mtk_wed_device *
+                         MTK_WED_CTRL_WED_RX_BM_BUSY);
+       mtk_wed_reset(dev, MTK_WED_RESET_RX_BM);
++      if (dev->wlan.hw_rro) {
++              wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN);
++              mtk_wed_poll_busy(dev, MTK_WED_CTRL,
++                                MTK_WED_CTRL_WED_RX_PG_BM_BUSY);
++              wed_set(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM);
++              wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM);
++      }
++
+       /* wo change to enable state */
+       val = MTK_WED_WO_STATE_ENABLE;
+       ret = mtk_wed_mcu_send_msg(wo, MTK_WED_MODULE_ID_WO,
+@@ -1493,6 +1714,7 @@ mtk_wed_rx_reset(struct mtk_wed_device *
+                                  false);
+       }
+       mtk_wed_free_rx_buffer(dev);
++      mtk_wed_hwrro_free_buffer(dev);
+       return 0;
+ }
+@@ -1526,15 +1748,41 @@ mtk_wed_reset_dma(struct mtk_wed_device
+       /* 2. reset WDMA rx DMA */
+       busy = !!mtk_wdma_rx_reset(dev);
+-      wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
++      if (mtk_wed_is_v3_or_greater(dev->hw)) {
++              val = MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE |
++                    wed_r32(dev, MTK_WED_WDMA_GLO_CFG);
++              val &= ~MTK_WED_WDMA_GLO_CFG_RX_DRV_EN;
++              wed_w32(dev, MTK_WED_WDMA_GLO_CFG, val);
++      } else {
++              wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
++                      MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
++      }
++
+       if (!busy)
+               busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG,
+                                        MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY);
++      if (!busy && mtk_wed_is_v3_or_greater(dev->hw))
++              busy = mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG,
++                                       MTK_WED_WDMA_RX_PREF_BUSY);
+       if (busy) {
+               mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
+               mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV);
+       } else {
++              if (mtk_wed_is_v3_or_greater(dev->hw)) {
++                      /* 1.a. disable prefetch HW */
++                      wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
++                              MTK_WED_WDMA_RX_PREF_EN);
++                      mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG,
++                                        MTK_WED_WDMA_RX_PREF_BUSY);
++                      wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
++                              MTK_WED_WDMA_RX_PREF_DDONE2_EN);
++
++                      /* 2. Reset dma index */
++                      wed_w32(dev, MTK_WED_WDMA_RESET_IDX,
++                              MTK_WED_WDMA_RESET_IDX_RX_ALL);
++              }
++
+               wed_w32(dev, MTK_WED_WDMA_RESET_IDX,
+                       MTK_WED_WDMA_RESET_IDX_RX | MTK_WED_WDMA_RESET_IDX_DRV);
+               wed_w32(dev, MTK_WED_WDMA_RESET_IDX, 0);
+@@ -1550,8 +1798,13 @@ mtk_wed_reset_dma(struct mtk_wed_device
+       wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+       for (i = 0; i < 100; i++) {
+-              val = wed_r32(dev, MTK_WED_TX_BM_INTF);
+-              if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40)
++              if (mtk_wed_is_v1(dev->hw))
++                      val = FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP,
++                                      wed_r32(dev, MTK_WED_TX_BM_INTF));
++              else
++                      val = FIELD_GET(MTK_WED_TX_TKID_INTF_TKFIFO_FDEP,
++                                      wed_r32(dev, MTK_WED_TX_TKID_INTF));
++              if (val == 0x40)
+                       break;
+       }
+@@ -1573,6 +1826,8 @@ mtk_wed_reset_dma(struct mtk_wed_device
+               mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
+               mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV);
+               mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_DRV);
++              if (mtk_wed_is_v3_or_greater(dev->hw))
++                      wed_w32(dev, MTK_WED_RX1_CTRL2, 0);
+       } else {
+               wed_w32(dev, MTK_WED_WPDMA_RESET_IDX,
+                       MTK_WED_WPDMA_RESET_IDX_TX |
+@@ -1589,7 +1844,14 @@ mtk_wed_reset_dma(struct mtk_wed_device
+               wed_w32(dev, MTK_WED_RESET_IDX, 0);
+       }
+-      mtk_wed_rx_reset(dev);
++      if (mtk_wed_is_v3_or_greater(dev->hw)) {
++              /* reset amsdu engine */
++              wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_AMSDU_EN);
++              mtk_wed_reset(dev, MTK_WED_RESET_TX_AMSDU);
++      }
++
++      if (mtk_wed_get_rx_capa(dev))
++              mtk_wed_rx_reset(dev);
+ }
+ static int
+@@ -1841,6 +2103,7 @@ mtk_wed_dma_enable(struct mtk_wed_device
+                       MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4);
+               wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
++              wdma_set(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
+       }
+       wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
+@@ -1904,6 +2167,12 @@ mtk_wed_start_hw_rro(struct mtk_wed_devi
+       if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hw_rro)
+               return;
++      if (reset) {
++              wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
++                      MTK_WED_RRO_MSDU_PG_DRV_EN);
++              return;
++      }
++
+       wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR);
+       wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
+               MTK_WED_RRO_MSDU_PG_DRV_CLR);
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -28,6 +28,8 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RESET                                 0x008
+ #define MTK_WED_RESET_TX_BM                           BIT(0)
+ #define MTK_WED_RESET_RX_BM                           BIT(1)
++#define MTK_WED_RESET_RX_PG_BM                                BIT(2)
++#define MTK_WED_RESET_RRO_RX_TO_PG                    BIT(3)
+ #define MTK_WED_RESET_TX_FREE_AGENT                   BIT(4)
+ #define MTK_WED_RESET_WPDMA_TX_DRV                    BIT(8)
+ #define MTK_WED_RESET_WPDMA_RX_DRV                    BIT(9)
+@@ -106,6 +108,9 @@ struct mtk_wdma_desc {
+ #define MTK_WED_STATUS                                        0x060
+ #define MTK_WED_STATUS_TX                             GENMASK(15, 8)
++#define MTK_WED_WPDMA_STATUS                          0x068
++#define MTK_WED_WPDMA_STATUS_TX_DRV                   GENMASK(15, 8)
++
+ #define MTK_WED_TX_BM_CTRL                            0x080
+ #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM                        GENMASK(6, 0)
+ #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM                        GENMASK(22, 16)
+@@ -140,6 +145,9 @@ struct mtk_wdma_desc {
+ #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM              GENMASK(22, 16)
+ #define MTK_WED_TX_TKID_CTRL_PAUSE                    BIT(28)
++#define MTK_WED_TX_TKID_INTF                          0x0dc
++#define MTK_WED_TX_TKID_INTF_TKFIFO_FDEP              GENMASK(25, 16)
++
+ #define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM_V3           GENMASK(7, 0)
+ #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM_V3           GENMASK(23, 16)
+@@ -190,6 +198,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RING_RX_DATA(_n)                      (0x420 + (_n) * 0x10)
+ #define MTK_WED_SCR0                                  0x3c0
++#define MTK_WED_RX1_CTRL2                             0x418
+ #define MTK_WED_WPDMA_INT_TRIGGER                     0x504
+ #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE             BIT(1)
+ #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE             GENMASK(5, 4)
+@@ -303,6 +312,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_RX_D_RST_IDX                    0x760
+ #define MTK_WED_WPDMA_RX_D_RST_CRX_IDX                        GENMASK(17, 16)
++#define MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL            BIT(20)
+ #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX                        GENMASK(25, 24)
+ #define MTK_WED_WPDMA_RX_GLO_CFG                      0x76c
+@@ -313,6 +323,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_RX_D_PREF_CFG                   0x7b4
+ #define MTK_WED_WPDMA_RX_D_PREF_EN                    BIT(0)
++#define MTK_WED_WPDMA_RX_D_PREF_BUSY                  BIT(1)
+ #define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE            GENMASK(12, 8)
+ #define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES             GENMASK(21, 16)
+@@ -334,11 +345,13 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WDMA_RX_PREF_CFG                      0x950
+ #define MTK_WED_WDMA_RX_PREF_EN                               BIT(0)
++#define MTK_WED_WDMA_RX_PREF_BUSY                     BIT(1)
+ #define MTK_WED_WDMA_RX_PREF_BURST_SIZE                       GENMASK(12, 8)
+ #define MTK_WED_WDMA_RX_PREF_LOW_THRES                        GENMASK(21, 16)
+ #define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR             BIT(24)
+ #define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR             BIT(25)
+ #define MTK_WED_WDMA_RX_PREF_DDONE2_EN                        BIT(26)
++#define MTK_WED_WDMA_RX_PREF_DDONE2_BUSY              BIT(27)
+ #define MTK_WED_WDMA_RX_PREF_FIFO_CFG                 0x95C
+ #define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR             BIT(0)
+@@ -367,6 +380,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WDMA_RESET_IDX                                0xa08
+ #define MTK_WED_WDMA_RESET_IDX_RX                     GENMASK(17, 16)
++#define MTK_WED_WDMA_RESET_IDX_RX_ALL                 BIT(20)
+ #define MTK_WED_WDMA_RESET_IDX_DRV                    GENMASK(25, 24)
+ #define MTK_WED_WDMA_INT_CLR                          0xa24
+@@ -437,21 +451,62 @@ struct mtk_wdma_desc {
+ #define MTK_WDMA_INT_MASK_RX_DELAY                    BIT(30)
+ #define MTK_WDMA_INT_MASK_RX_COHERENT                 BIT(31)
++#define MTK_WDMA_XDMA_TX_FIFO_CFG                     0x238
++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR   BIT(0)
++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR   BIT(4)
++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR  BIT(8)
++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR   BIT(12)
++
++#define MTK_WDMA_XDMA_RX_FIFO_CFG                     0x23c
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR   BIT(0)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR   BIT(4)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR  BIT(8)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR   BIT(12)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR   BIT(15)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR   BIT(18)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR   BIT(21)
++
+ #define MTK_WDMA_INT_GRP1                             0x250
+ #define MTK_WDMA_INT_GRP2                             0x254
+ #define MTK_WDMA_PREF_TX_CFG                          0x2d0
+ #define MTK_WDMA_PREF_TX_CFG_PREF_EN                  BIT(0)
++#define MTK_WDMA_PREF_TX_CFG_PREF_BUSY                        BIT(1)
+ #define MTK_WDMA_PREF_RX_CFG                          0x2dc
+ #define MTK_WDMA_PREF_RX_CFG_PREF_EN                  BIT(0)
++#define MTK_WDMA_PREF_RX_CFG_PREF_BUSY                        BIT(1)
++
++#define MTK_WDMA_PREF_RX_FIFO_CFG                     0x2e0
++#define MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR         BIT(0)
++#define MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR         BIT(16)
++
++#define MTK_WDMA_PREF_TX_FIFO_CFG                     0x2d4
++#define MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR         BIT(0)
++#define MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR         BIT(16)
++
++#define MTK_WDMA_PREF_SIDX_CFG                                0x2e4
++#define MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR          GENMASK(3, 0)
++#define MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR          GENMASK(5, 4)
+ #define MTK_WDMA_WRBK_TX_CFG                          0x300
++#define MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY                        BIT(0)
+ #define MTK_WDMA_WRBK_TX_CFG_WRBK_EN                  BIT(30)
++#define MTK_WDMA_WRBK_TX_FIFO_CFG(_n)                 (0x304 + (_n) * 0x4)
++#define MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR          BIT(0)
++
+ #define MTK_WDMA_WRBK_RX_CFG                          0x344
++#define MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY                        BIT(0)
+ #define MTK_WDMA_WRBK_RX_CFG_WRBK_EN                  BIT(30)
++#define MTK_WDMA_WRBK_RX_FIFO_CFG(_n)                 (0x348 + (_n) * 0x4)
++#define MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR          BIT(0)
++
++#define MTK_WDMA_WRBK_SIDX_CFG                                0x388
++#define MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR          GENMASK(3, 0)
++#define MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR          GENMASK(5, 4)
++
+ #define MTK_PCIE_MIRROR_MAP(n)                                ((n) ? 0x4 : 0x0)
+ #define MTK_PCIE_MIRROR_MAP_EN                                BIT(0)
+ #define MTK_PCIE_MIRROR_MAP_WED_ID                    BIT(1)
+@@ -465,6 +520,8 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RTQM_Q_DBG_BYPASS                     BIT(5)
+ #define MTK_WED_RTQM_TXDMAD_FPORT                     GENMASK(23, 20)
++#define MTK_WED_RTQM_RST                              0xb04
++
+ #define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT              0xb1c
+ #define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n)           (0xb20 + (_n) * 0x4)
+ #define       MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT                 0xb28
+@@ -653,6 +710,9 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR            BIT(17)
+ #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG      GENMASK(22, 18)
++#define MTK_WED_RRO_RX_HW_STS                         0xf00
++#define MTK_WED_RX_IND_CMD_BUSY                               GENMASK(31, 0)
++
+ #define MTK_WED_RX_IND_CMD_CNT0                               0xf20
+ #define MTK_WED_RX_IND_CMD_DBG_CNT_EN                 BIT(31)
diff --git a/target/linux/generic/backport-6.6/752-21-v6.7-net-ethernet-mtk_wed-fix-firmware-loading-for-MT7986.patch b/target/linux/generic/backport-6.6/752-21-v6.7-net-ethernet-mtk_wed-fix-firmware-loading-for-MT7986.patch
new file mode 100644 (file)
index 0000000..3b0795c
--- /dev/null
@@ -0,0 +1,113 @@
+From 52ea72ad0daa0f29535b4cef39257616c5a211d3 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Tue, 24 Oct 2023 00:00:19 +0200
+Subject: [PATCH 1/5] net: ethernet: mtk_wed: fix firmware loading for MT7986
+ SoC
+
+The WED mcu firmware does not contain all the memory regions defined in
+the dts reserved_memory node (e.g. MT7986 WED firmware does not contain
+cpu-boot region).
+Reverse the mtk_wed_mcu_run_firmware() logic to check all the fw
+sections are defined in the dts reserved_memory node.
+
+Fixes: c6d961aeaa77 ("net: ethernet: mtk_wed: move mem_region array out of mtk_wed_mcu_load_firmware")
+Tested-by: Frank Wunderlich <frank-w@public-files.de>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://lore.kernel.org/r/d983cbfe8ea562fef9264de8f0c501f7d5705bd5.1698098381.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 48 +++++++++++----------
+ 1 file changed, 25 insertions(+), 23 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -258,16 +258,12 @@ mtk_wed_get_memory_region(struct mtk_wed
+ }
+ static int
+-mtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw,
+-                       struct mtk_wed_wo_memory_region *region)
++mtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw)
+ {
+       const u8 *first_region_ptr, *region_ptr, *trailer_ptr, *ptr = fw->data;
+       const struct mtk_wed_fw_trailer *trailer;
+       const struct mtk_wed_fw_region *fw_region;
+-      if (!region->phy_addr || !region->size)
+-              return 0;
+-
+       trailer_ptr = fw->data + fw->size - sizeof(*trailer);
+       trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr;
+       region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region);
+@@ -275,33 +271,41 @@ mtk_wed_mcu_run_firmware(struct mtk_wed_
+       while (region_ptr < trailer_ptr) {
+               u32 length;
++              int i;
+               fw_region = (const struct mtk_wed_fw_region *)region_ptr;
+               length = le32_to_cpu(fw_region->len);
+-
+-              if (region->phy_addr != le32_to_cpu(fw_region->addr))
+-                      goto next;
+-
+-              if (region->size < length)
+-                      goto next;
+-
+               if (first_region_ptr < ptr + length)
+                       goto next;
+-              if (region->shared && region->consumed)
+-                      return 0;
++              for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
++                      struct mtk_wed_wo_memory_region *region;
+-              if (!region->shared || !region->consumed) {
+-                      memcpy_toio(region->addr, ptr, length);
+-                      region->consumed = true;
+-                      return 0;
++                      region = &mem_region[i];
++                      if (region->phy_addr != le32_to_cpu(fw_region->addr))
++                              continue;
++
++                      if (region->size < length)
++                              continue;
++
++                      if (region->shared && region->consumed)
++                              break;
++
++                      if (!region->shared || !region->consumed) {
++                              memcpy_toio(region->addr, ptr, length);
++                              region->consumed = true;
++                              break;
++                      }
+               }
++
++              if (i == ARRAY_SIZE(mem_region))
++                      return -EINVAL;
+ next:
+               region_ptr += sizeof(*fw_region);
+               ptr += length;
+       }
+-      return -EINVAL;
++      return 0;
+ }
+ static int
+@@ -360,11 +364,9 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
+       dev_info(wo->hw->dev, "MTK WED WO Chip ID %02x Region %d\n",
+                trailer->chip_id, trailer->num_region);
+-      for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
+-              ret = mtk_wed_mcu_run_firmware(wo, fw, &mem_region[i]);
+-              if (ret)
+-                      goto out;
+-      }
++      ret = mtk_wed_mcu_run_firmware(wo, fw);
++      if (ret)
++              goto out;
+       /* set the start address */
+       if (!mtk_wed_is_v3_or_greater(wo->hw) && wo->hw->index)
diff --git a/target/linux/generic/backport-6.6/752-22-v6.7-net-ethernet-mtk_wed-remove-wo-pointer-in-wo_r32-wo_.patch b/target/linux/generic/backport-6.6/752-22-v6.7-net-ethernet-mtk_wed-remove-wo-pointer-in-wo_r32-wo_.patch
new file mode 100644 (file)
index 0000000..c1bb3f5
--- /dev/null
@@ -0,0 +1,51 @@
+From 7aa8defd3495208289abcc629946af26a2af3391 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Tue, 24 Oct 2023 00:01:30 +0200
+Subject: [PATCH 2/5] net: ethernet: mtk_wed: remove wo pointer in
+ wo_r32/wo_w32 signature
+
+wo pointer is no longer used in wo_r32 and wo_w32 routines so get rid of
+it.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/530537db0872f7523deff21f0a5dfdd9b75fdc9d.1698098459.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_wed_mcu.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -32,12 +32,12 @@ static struct mtk_wed_wo_memory_region m
+       },
+ };
+-static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg)
++static u32 wo_r32(u32 reg)
+ {
+       return readl(mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
+ }
+-static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
++static void wo_w32(u32 reg, u32 val)
+ {
+       writel(val, mem_region[MTK_WED_WO_REGION_BOOT].addr + reg);
+ }
+@@ -373,13 +373,13 @@ mtk_wed_mcu_load_firmware(struct mtk_wed
+               boot_cr = MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR;
+       else
+               boot_cr = MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
+-      wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16);
++      wo_w32(boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16);
+       /* wo firmware reset */
+-      wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00);
++      wo_w32(MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00);
+-      val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) |
++      val = wo_r32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR) |
+             MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK;
+-      wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
++      wo_w32(MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
+ out:
+       release_firmware(fw);
diff --git a/target/linux/generic/backport-6.6/752-23-v6.8-net-ethernet-mtk_wed-rely-on-__dev_alloc_page-in-mtk.patch b/target/linux/generic/backport-6.6/752-23-v6.8-net-ethernet-mtk_wed-rely-on-__dev_alloc_page-in-mtk.patch
new file mode 100644 (file)
index 0000000..7cbf6bd
--- /dev/null
@@ -0,0 +1,26 @@
+From 65aacd457eaf5d0c958ed8030ec46f99ea808dd9 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 Nov 2023 17:39:22 +0100
+Subject: [PATCH 3/5] net: ethernet: mtk_wed: rely on __dev_alloc_page in
+ mtk_wed_tx_buffer_alloc
+
+Simplify the code and use __dev_alloc_page() instead of __dev_alloc_pages()
+with order 0 in mtk_wed_tx_buffer_alloc routine
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_wed.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -670,7 +670,7 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
+               void *buf;
+               int s;
+-              page = __dev_alloc_pages(GFP_KERNEL, 0);
++              page = __dev_alloc_page(GFP_KERNEL);
+               if (!page)
+                       return -ENOMEM;
diff --git a/target/linux/generic/backport-6.6/752-24-v6.8-net-ethernet-mtk_wed-add-support-for-devices-with-mo.patch b/target/linux/generic/backport-6.6/752-24-v6.8-net-ethernet-mtk_wed-add-support-for-devices-with-mo.patch
new file mode 100644 (file)
index 0000000..b08f3aa
--- /dev/null
@@ -0,0 +1,91 @@
+From 5f5997322584b6257543d4d103f81484b8006d84 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 17 Nov 2023 17:42:59 +0100
+Subject: [PATCH 4/5] net: ethernet: mtk_wed: add support for devices with more
+ than 4GB of dram
+
+Introduce WED offloading support for boards with more than 4GB of
+memory.
+
+Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/1c7efdf5d384ea7af3c0209723e40b2ee0f956bf.1700239272.git.lorenzo@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 ++++-
+ drivers/net/ethernet/mediatek/mtk_wed.c     | 8 +++++---
+ drivers/net/ethernet/mediatek/mtk_wed_wo.c  | 3 ++-
+ 3 files changed, 11 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1158,15 +1158,18 @@ static int mtk_init_fq_dma(struct mtk_et
+       phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1);
+       for (i = 0; i < cnt; i++) {
++              dma_addr_t addr = dma_addr + i * MTK_QDMA_PAGE_SIZE;
+               struct mtk_tx_dma_v2 *txd;
+               txd = eth->scratch_ring + i * soc->tx.desc_size;
+-              txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
++              txd->txd1 = addr;
+               if (i < cnt - 1)
+                       txd->txd2 = eth->phy_scratch_ring +
+                                   (i + 1) * soc->tx.desc_size;
+               txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
++              if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA))
++                      txd->txd3 |= TX_DMA_PREP_ADDR64(addr);
+               txd->txd4 = 0;
+               if (mtk_is_netsys_v2_or_greater(eth)) {
+                       txd->txd5 = 0;
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -691,10 +691,11 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
+               for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) {
+                       struct mtk_wdma_desc *desc = desc_ptr;
++                      u32 ctrl;
+                       desc->buf0 = cpu_to_le32(buf_phys);
+                       if (!mtk_wed_is_v3_or_greater(dev->hw)) {
+-                              u32 txd_size, ctrl;
++                              u32 txd_size;
+                               txd_size = dev->wlan.init_buf(buf, buf_phys,
+                                                             token++);
+@@ -708,11 +709,11 @@ mtk_wed_tx_buffer_alloc(struct mtk_wed_d
+                                       ctrl |= MTK_WDMA_DESC_CTRL_LAST_SEG0 |
+                                               FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1_V2,
+                                                          MTK_WED_BUF_SIZE - txd_size);
+-                              desc->ctrl = cpu_to_le32(ctrl);
+                               desc->info = 0;
+                       } else {
+-                              desc->ctrl = cpu_to_le32(token << 16);
++                              ctrl = token << 16 | TX_DMA_PREP_ADDR64(buf_phys);
+                       }
++                      desc->ctrl = cpu_to_le32(ctrl);
+                       desc_ptr += desc_size;
+                       buf += MTK_WED_BUF_SIZE;
+@@ -811,6 +812,7 @@ mtk_wed_hwrro_buffer_alloc(struct mtk_we
+               buf_phys = page_phys;
+               for (s = 0; s < MTK_WED_RX_BUF_PER_PAGE; s++) {
+                       desc->buf0 = cpu_to_le32(buf_phys);
++                      desc->token = cpu_to_le32(RX_DMA_PREP_ADDR64(buf_phys));
+                       buf_phys += MTK_WED_PAGE_BUF_SIZE;
+                       desc++;
+               }
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
+@@ -142,7 +142,8 @@ mtk_wed_wo_queue_refill(struct mtk_wed_w
+               dma_addr_t addr;
+               void *buf;
+-              buf = page_frag_alloc(&q->cache, q->buf_size, GFP_ATOMIC);
++              buf = page_frag_alloc(&q->cache, q->buf_size,
++                                    GFP_ATOMIC | GFP_DMA32);
+               if (!buf)
+                       break;
diff --git a/target/linux/generic/backport-6.6/752-25-v6.10-net-ethernet-mtk_eth_soc-handle-dma-buffer-size-soc-.patch b/target/linux/generic/backport-6.6/752-25-v6.10-net-ethernet-mtk_eth_soc-handle-dma-buffer-size-soc-.patch
new file mode 100644 (file)
index 0000000..3d72a5f
--- /dev/null
@@ -0,0 +1,364 @@
+From c57e558194430d10d5e5f4acd8a8655b68dade13 Mon Sep 17 00:00:00 2001
+From: Frank Wunderlich <frank-w@public-files.de>
+Date: Mon, 3 Jun 2024 21:25:05 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: handle dma buffer size soc
+ specific
+
+The mainline MTK ethernet driver suffers long time from rarly but
+annoying tx queue timeouts. We think that this is caused by fixed
+dma sizes hardcoded for all SoCs.
+
+We suspect this problem arises from a low level of free TX DMADs,
+the TX Ring alomost full.
+
+The transmit timeout is caused by the Tx queue not waking up. The
+Tx queue stops when the free counter is less than ring->thres, and
+it will wake up once the free counter is greater than ring->thres.
+If the CPU is too late to wake up the Tx queues, it may cause a
+transmit timeout.
+Therefore, we increased the TX and RX DMADs to improve this error
+situation.
+
+Use the dma-size implementation from SDK in a per SoC manner. In
+difference to SDK we have no RSS feature yet, so all RX/TX sizes
+should be raised from 512 to 2048 byte except fqdma on mt7988 to
+avoid the tx timeout issue.
+
+Fixes: 656e705243fd ("net-next: mediatek: add support for MT7623 ethernet")
+Suggested-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 104 +++++++++++++-------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h |   9 +-
+ 2 files changed, 77 insertions(+), 36 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1131,9 +1131,9 @@ static int mtk_init_fq_dma(struct mtk_et
+ {
+       const struct mtk_soc_data *soc = eth->soc;
+       dma_addr_t phy_ring_tail;
+-      int cnt = MTK_QDMA_RING_SIZE;
++      int cnt = soc->tx.fq_dma_size;
+       dma_addr_t dma_addr;
+-      int i;
++      int i, j, len;
+       if (MTK_HAS_CAPS(eth->soc->caps, MTK_SRAM))
+               eth->scratch_ring = eth->sram_base;
+@@ -1142,40 +1142,46 @@ static int mtk_init_fq_dma(struct mtk_et
+                                                      cnt * soc->tx.desc_size,
+                                                      &eth->phy_scratch_ring,
+                                                      GFP_KERNEL);
++
+       if (unlikely(!eth->scratch_ring))
+               return -ENOMEM;
+-      eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE, GFP_KERNEL);
+-      if (unlikely(!eth->scratch_head))
+-              return -ENOMEM;
++      phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1);
+-      dma_addr = dma_map_single(eth->dma_dev,
+-                                eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
+-                                DMA_FROM_DEVICE);
+-      if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
+-              return -ENOMEM;
++      for (j = 0; j < DIV_ROUND_UP(soc->tx.fq_dma_size, MTK_FQ_DMA_LENGTH); j++) {
++              len = min_t(int, cnt - j * MTK_FQ_DMA_LENGTH, MTK_FQ_DMA_LENGTH);
++              eth->scratch_head[j] = kcalloc(len, MTK_QDMA_PAGE_SIZE, GFP_KERNEL);
+-      phy_ring_tail = eth->phy_scratch_ring + soc->tx.desc_size * (cnt - 1);
++              if (unlikely(!eth->scratch_head[j]))
++                      return -ENOMEM;
+-      for (i = 0; i < cnt; i++) {
+-              dma_addr_t addr = dma_addr + i * MTK_QDMA_PAGE_SIZE;
+-              struct mtk_tx_dma_v2 *txd;
+-
+-              txd = eth->scratch_ring + i * soc->tx.desc_size;
+-              txd->txd1 = addr;
+-              if (i < cnt - 1)
+-                      txd->txd2 = eth->phy_scratch_ring +
+-                                  (i + 1) * soc->tx.desc_size;
+-
+-              txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
+-              if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA))
+-                      txd->txd3 |= TX_DMA_PREP_ADDR64(addr);
+-              txd->txd4 = 0;
+-              if (mtk_is_netsys_v2_or_greater(eth)) {
+-                      txd->txd5 = 0;
+-                      txd->txd6 = 0;
+-                      txd->txd7 = 0;
+-                      txd->txd8 = 0;
++              dma_addr = dma_map_single(eth->dma_dev,
++                                        eth->scratch_head[j], len * MTK_QDMA_PAGE_SIZE,
++                                        DMA_FROM_DEVICE);
++
++              if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
++                      return -ENOMEM;
++
++              for (i = 0; i < cnt; i++) {
++                      struct mtk_tx_dma_v2 *txd;
++
++                      txd = eth->scratch_ring + (j * MTK_FQ_DMA_LENGTH + i) * soc->tx.desc_size;
++                      txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE;
++                      if (j * MTK_FQ_DMA_LENGTH + i < cnt)
++                              txd->txd2 = eth->phy_scratch_ring +
++                                          (j * MTK_FQ_DMA_LENGTH + i + 1) * soc->tx.desc_size;
++
++                      txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE);
++                      if (MTK_HAS_CAPS(soc->caps, MTK_36BIT_DMA))
++                              txd->txd3 |= TX_DMA_PREP_ADDR64(dma_addr + i * MTK_QDMA_PAGE_SIZE);
++
++                      txd->txd4 = 0;
++                      if (mtk_is_netsys_v2_or_greater(eth)) {
++                              txd->txd5 = 0;
++                              txd->txd6 = 0;
++                              txd->txd7 = 0;
++                              txd->txd8 = 0;
++                      }
+               }
+       }
+@@ -2457,7 +2463,7 @@ static int mtk_tx_alloc(struct mtk_eth *
+       if (MTK_HAS_CAPS(soc->caps, MTK_QDMA))
+               ring_size = MTK_QDMA_RING_SIZE;
+       else
+-              ring_size = MTK_DMA_SIZE;
++              ring_size = soc->tx.dma_size;
+       ring->buf = kcalloc(ring_size, sizeof(*ring->buf),
+                              GFP_KERNEL);
+@@ -2465,8 +2471,8 @@ static int mtk_tx_alloc(struct mtk_eth *
+               goto no_tx_mem;
+       if (MTK_HAS_CAPS(soc->caps, MTK_SRAM)) {
+-              ring->dma = eth->sram_base + ring_size * sz;
+-              ring->phys = eth->phy_scratch_ring + ring_size * (dma_addr_t)sz;
++              ring->dma = eth->sram_base + soc->tx.fq_dma_size * sz;
++              ring->phys = eth->phy_scratch_ring + soc->tx.fq_dma_size * (dma_addr_t)sz;
+       } else {
+               ring->dma = dma_alloc_coherent(eth->dma_dev, ring_size * sz,
+                                              &ring->phys, GFP_KERNEL);
+@@ -2588,6 +2594,7 @@ static void mtk_tx_clean(struct mtk_eth
+ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag)
+ {
+       const struct mtk_reg_map *reg_map = eth->soc->reg_map;
++      const struct mtk_soc_data *soc = eth->soc;
+       struct mtk_rx_ring *ring;
+       int rx_data_len, rx_dma_size, tx_ring_size;
+       int i;
+@@ -2595,7 +2602,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+       if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
+               tx_ring_size = MTK_QDMA_RING_SIZE;
+       else
+-              tx_ring_size = MTK_DMA_SIZE;
++              tx_ring_size = soc->tx.dma_size;
+       if (rx_flag == MTK_RX_FLAGS_QDMA) {
+               if (ring_no)
+@@ -2610,7 +2617,7 @@ static int mtk_rx_alloc(struct mtk_eth *
+               rx_dma_size = MTK_HW_LRO_DMA_SIZE;
+       } else {
+               rx_data_len = ETH_DATA_LEN;
+-              rx_dma_size = MTK_DMA_SIZE;
++              rx_dma_size = soc->rx.dma_size;
+       }
+       ring->frag_size = mtk_max_frag_size(rx_data_len);
+@@ -3139,7 +3146,10 @@ static void mtk_dma_free(struct mtk_eth
+                       mtk_rx_clean(eth, &eth->rx_ring[i], false);
+       }
+-      kfree(eth->scratch_head);
++      for (i = 0; i < DIV_ROUND_UP(soc->tx.fq_dma_size, MTK_FQ_DMA_LENGTH); i++) {
++              kfree(eth->scratch_head[i]);
++              eth->scratch_head[i] = NULL;
++      }
+ }
+ static bool mtk_hw_reset_check(struct mtk_eth *eth)
+@@ -5045,11 +5055,14 @@ static const struct mtk_soc_data mt2701_
+               .desc_size = sizeof(struct mtk_tx_dma),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
++              .dma_size = MTK_DMA_SIZE(2K),
++              .fq_dma_size = MTK_DMA_SIZE(2K),
+       },
+       .rx = {
+               .desc_size = sizeof(struct mtk_rx_dma),
+               .irq_done_mask = MTK_RX_DONE_INT,
+               .dma_l4_valid = RX_DMA_L4_VALID,
++              .dma_size = MTK_DMA_SIZE(2K),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5069,11 +5082,14 @@ static const struct mtk_soc_data mt7621_
+               .desc_size = sizeof(struct mtk_tx_dma),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
++              .dma_size = MTK_DMA_SIZE(2K),
++              .fq_dma_size = MTK_DMA_SIZE(2K),
+       },
+       .rx = {
+               .desc_size = sizeof(struct mtk_rx_dma),
+               .irq_done_mask = MTK_RX_DONE_INT,
+               .dma_l4_valid = RX_DMA_L4_VALID,
++              .dma_size = MTK_DMA_SIZE(2K),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5095,11 +5111,14 @@ static const struct mtk_soc_data mt7622_
+               .desc_size = sizeof(struct mtk_tx_dma),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
++              .dma_size = MTK_DMA_SIZE(2K),
++              .fq_dma_size = MTK_DMA_SIZE(2K),
+       },
+       .rx = {
+               .desc_size = sizeof(struct mtk_rx_dma),
+               .irq_done_mask = MTK_RX_DONE_INT,
+               .dma_l4_valid = RX_DMA_L4_VALID,
++              .dma_size = MTK_DMA_SIZE(2K),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5120,11 +5139,14 @@ static const struct mtk_soc_data mt7623_
+               .desc_size = sizeof(struct mtk_tx_dma),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
++              .dma_size = MTK_DMA_SIZE(2K),
++              .fq_dma_size = MTK_DMA_SIZE(2K),
+       },
+       .rx = {
+               .desc_size = sizeof(struct mtk_rx_dma),
+               .irq_done_mask = MTK_RX_DONE_INT,
+               .dma_l4_valid = RX_DMA_L4_VALID,
++              .dma_size = MTK_DMA_SIZE(2K),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5143,11 +5165,14 @@ static const struct mtk_soc_data mt7629_
+               .desc_size = sizeof(struct mtk_tx_dma),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
++              .dma_size = MTK_DMA_SIZE(2K),
++              .fq_dma_size = MTK_DMA_SIZE(2K),
+       },
+       .rx = {
+               .desc_size = sizeof(struct mtk_rx_dma),
+               .irq_done_mask = MTK_RX_DONE_INT,
+               .dma_l4_valid = RX_DMA_L4_VALID,
++              .dma_size = MTK_DMA_SIZE(2K),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
+       },
+@@ -5169,6 +5194,8 @@ static const struct mtk_soc_data mt7981_
+               .desc_size = sizeof(struct mtk_tx_dma_v2),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+               .dma_len_offset = 8,
++              .dma_size = MTK_DMA_SIZE(2K),
++              .fq_dma_size = MTK_DMA_SIZE(2K),
+       },
+       .rx = {
+               .desc_size = sizeof(struct mtk_rx_dma),
+@@ -5176,6 +5203,7 @@ static const struct mtk_soc_data mt7981_
+               .dma_l4_valid = RX_DMA_L4_VALID_V2,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
++              .dma_size = MTK_DMA_SIZE(2K),
+       },
+ };
+@@ -5195,6 +5223,8 @@ static const struct mtk_soc_data mt7986_
+               .desc_size = sizeof(struct mtk_tx_dma_v2),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+               .dma_len_offset = 8,
++              .dma_size = MTK_DMA_SIZE(2K),
++              .fq_dma_size = MTK_DMA_SIZE(2K),
+       },
+       .rx = {
+               .desc_size = sizeof(struct mtk_rx_dma),
+@@ -5202,6 +5232,7 @@ static const struct mtk_soc_data mt7986_
+               .dma_l4_valid = RX_DMA_L4_VALID_V2,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
++              .dma_size = MTK_DMA_SIZE(2K),
+       },
+ };
+@@ -5221,6 +5252,8 @@ static const struct mtk_soc_data mt7988_
+               .desc_size = sizeof(struct mtk_tx_dma_v2),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+               .dma_len_offset = 8,
++              .dma_size = MTK_DMA_SIZE(2K),
++              .fq_dma_size = MTK_DMA_SIZE(4K),
+       },
+       .rx = {
+               .desc_size = sizeof(struct mtk_rx_dma_v2),
+@@ -5228,6 +5261,7 @@ static const struct mtk_soc_data mt7988_
+               .dma_l4_valid = RX_DMA_L4_VALID_V2,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN_V2,
+               .dma_len_offset = 8,
++              .dma_size = MTK_DMA_SIZE(2K),
+       },
+ };
+@@ -5242,6 +5276,7 @@ static const struct mtk_soc_data rt5350_
+               .desc_size = sizeof(struct mtk_tx_dma),
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
++              .dma_size = MTK_DMA_SIZE(2K),
+       },
+       .rx = {
+               .desc_size = sizeof(struct mtk_rx_dma),
+@@ -5249,6 +5284,7 @@ static const struct mtk_soc_data rt5350_
+               .dma_l4_valid = RX_DMA_L4_VALID_PDMA,
+               .dma_max_len = MTK_TX_DMA_BUF_LEN,
+               .dma_len_offset = 16,
++              .dma_size = MTK_DMA_SIZE(2K),
+       },
+ };
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -32,7 +32,9 @@
+ #define MTK_TX_DMA_BUF_LEN    0x3fff
+ #define MTK_TX_DMA_BUF_LEN_V2 0xffff
+ #define MTK_QDMA_RING_SIZE    2048
+-#define MTK_DMA_SIZE          512
++#define MTK_DMA_SIZE(x)               (SZ_##x)
++#define MTK_FQ_DMA_HEAD               32
++#define MTK_FQ_DMA_LENGTH     2048
+ #define MTK_RX_ETH_HLEN               (ETH_HLEN + ETH_FCS_LEN)
+ #define MTK_RX_HLEN           (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
+ #define MTK_DMA_DUMMY_DESC    0xffffffff
+@@ -1176,6 +1178,8 @@ struct mtk_soc_data {
+               u32     desc_size;
+               u32     dma_max_len;
+               u32     dma_len_offset;
++              u32     dma_size;
++              u32     fq_dma_size;
+       } tx;
+       struct {
+               u32     desc_size;
+@@ -1183,6 +1187,7 @@ struct mtk_soc_data {
+               u32     dma_l4_valid;
+               u32     dma_max_len;
+               u32     dma_len_offset;
++              u32     dma_size;
+       } rx;
+ };
+@@ -1264,7 +1269,7 @@ struct mtk_eth {
+       struct napi_struct              rx_napi;
+       void                            *scratch_ring;
+       dma_addr_t                      phy_scratch_ring;
+-      void                            *scratch_head;
++      void                            *scratch_head[MTK_FQ_DMA_HEAD];
+       struct clk                      *clks[MTK_CLK_MAX];
+       struct mii_bus                  *mii_bus;
diff --git a/target/linux/generic/backport-6.6/752-26-v6.10-net-ethernet-mtk_eth_soc-ppe-add-support-for-multipl.patch b/target/linux/generic/backport-6.6/752-26-v6.10-net-ethernet-mtk_eth_soc-ppe-add-support-for-multipl.patch
new file mode 100644 (file)
index 0000000..07e7e86
--- /dev/null
@@ -0,0 +1,371 @@
+From dee4dd10c79aaca192b73520d8fb64628468ae0f Mon Sep 17 00:00:00 2001
+From: Elad Yifee <eladwf@gmail.com>
+Date: Fri, 7 Jun 2024 11:21:50 +0300
+Subject: [PATCH] net: ethernet: mtk_eth_soc: ppe: add support for multiple
+ PPEs
+
+Add the missing pieces to allow multiple PPEs units, one for each GMAC.
+mtk_gdm_config has been modified to work on targted mac ID,
+the inner loop moved outside of the function to allow unrelated
+operations like setting the MAC's PPE index.
+Introduce a sanity check in flow_offload_replace to account for
+non-MTK ingress devices.
+Additional field 'ppe_idx' was added to struct mtk_mac in order
+to keep track on the assigned PPE unit.
+
+Signed-off-by: Elad Yifee <eladwf@gmail.com>
+Link: https://lore.kernel.org/r/20240607082155.20021-1-eladwf@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c   | 112 +++++++++++-------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h   |   8 +-
+ .../net/ethernet/mediatek/mtk_ppe_offload.c   |  17 ++-
+ 3 files changed, 92 insertions(+), 45 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -80,7 +80,9 @@ static const struct mtk_reg_map mtk_reg_
+               .fq_blen        = 0x1b2c,
+       },
+       .gdm1_cnt               = 0x2400,
+-      .gdma_to_ppe            = 0x4444,
++      .gdma_to_ppe    = {
++              [0]             = 0x4444,
++      },
+       .ppe_base               = 0x0c00,
+       .wdma_base = {
+               [0]             = 0x2800,
+@@ -144,7 +146,10 @@ static const struct mtk_reg_map mt7986_r
+               .tx_sch_rate    = 0x4798,
+       },
+       .gdm1_cnt               = 0x1c00,
+-      .gdma_to_ppe            = 0x3333,
++      .gdma_to_ppe    = {
++              [0]             = 0x3333,
++              [1]             = 0x4444,
++      },
+       .ppe_base               = 0x2000,
+       .wdma_base = {
+               [0]             = 0x4800,
+@@ -192,7 +197,11 @@ static const struct mtk_reg_map mt7988_r
+               .tx_sch_rate    = 0x4798,
+       },
+       .gdm1_cnt               = 0x1c00,
+-      .gdma_to_ppe            = 0x3333,
++      .gdma_to_ppe    = {
++              [0]             = 0x3333,
++              [1]             = 0x4444,
++              [2]             = 0xcccc,
++      },
+       .ppe_base               = 0x2000,
+       .wdma_base = {
+               [0]             = 0x4800,
+@@ -2015,6 +2024,7 @@ static int mtk_poll_rx(struct napi_struc
+       struct mtk_rx_dma_v2 *rxd, trxd;
+       int done = 0, bytes = 0;
+       dma_addr_t dma_addr = DMA_MAPPING_ERROR;
++      int ppe_idx = 0;
+       while (done < budget) {
+               unsigned int pktlen, *rxdcsum;
+@@ -2058,6 +2068,7 @@ static int mtk_poll_rx(struct napi_struc
+                       goto release_desc;
+               netdev = eth->netdev[mac];
++              ppe_idx = eth->mac[mac]->ppe_idx;
+               if (unlikely(test_bit(MTK_RESETTING, &eth->state)))
+                       goto release_desc;
+@@ -2181,7 +2192,7 @@ static int mtk_poll_rx(struct napi_struc
+               }
+               if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
+-                      mtk_ppe_check_skb(eth->ppe[0], skb, hash);
++                      mtk_ppe_check_skb(eth->ppe[ppe_idx], skb, hash);
+               skb_record_rx_queue(skb, 0);
+               napi_gro_receive(napi, skb);
+@@ -3276,37 +3287,27 @@ static int mtk_start_dma(struct mtk_eth
+       return 0;
+ }
+-static void mtk_gdm_config(struct mtk_eth *eth, u32 config)
++static void mtk_gdm_config(struct mtk_eth *eth, u32 id, u32 config)
+ {
+-      int i;
++      u32 val;
+       if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+               return;
+-      for (i = 0; i < MTK_MAX_DEVS; i++) {
+-              u32 val;
+-
+-              if (!eth->netdev[i])
+-                      continue;
++      val = mtk_r32(eth, MTK_GDMA_FWD_CFG(id));
+-              val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
++      /* default setup the forward port to send frame to PDMA */
++      val &= ~0xffff;
+-              /* default setup the forward port to send frame to PDMA */
+-              val &= ~0xffff;
++      /* Enable RX checksum */
++      val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN;
+-              /* Enable RX checksum */
+-              val |= MTK_GDMA_ICS_EN | MTK_GDMA_TCS_EN | MTK_GDMA_UCS_EN;
++      val |= config;
+-              val |= config;
++      if (eth->netdev[id] && netdev_uses_dsa(eth->netdev[id]))
++              val |= MTK_GDMA_SPECIAL_TAG;
+-              if (netdev_uses_dsa(eth->netdev[i]))
+-                      val |= MTK_GDMA_SPECIAL_TAG;
+-
+-              mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
+-      }
+-      /* Reset and enable PSE */
+-      mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
+-      mtk_w32(eth, 0, MTK_RST_GL);
++      mtk_w32(eth, val, MTK_GDMA_FWD_CFG(id));
+ }
+@@ -3366,7 +3367,10 @@ static int mtk_open(struct net_device *d
+ {
+       struct mtk_mac *mac = netdev_priv(dev);
+       struct mtk_eth *eth = mac->hw;
+-      int i, err;
++      struct mtk_mac *target_mac;
++      int i, err, ppe_num;
++
++      ppe_num = eth->soc->ppe_num;
+       err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
+       if (err) {
+@@ -3390,18 +3394,38 @@ static int mtk_open(struct net_device *d
+               for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
+                       mtk_ppe_start(eth->ppe[i]);
+-              gdm_config = soc->offload_version ? soc->reg_map->gdma_to_ppe
+-                                                : MTK_GDMA_TO_PDMA;
+-              mtk_gdm_config(eth, gdm_config);
++              for (i = 0; i < MTK_MAX_DEVS; i++) {
++                      if (!eth->netdev[i])
++                              break;
++
++                      target_mac = netdev_priv(eth->netdev[i]);
++                      if (!soc->offload_version) {
++                              target_mac->ppe_idx = 0;
++                              gdm_config = MTK_GDMA_TO_PDMA;
++                      } else if (ppe_num >= 3 && target_mac->id == 2) {
++                              target_mac->ppe_idx = 2;
++                              gdm_config = soc->reg_map->gdma_to_ppe[2];
++                      } else if (ppe_num >= 2 && target_mac->id == 1) {
++                              target_mac->ppe_idx = 1;
++                              gdm_config = soc->reg_map->gdma_to_ppe[1];
++                      } else {
++                              target_mac->ppe_idx = 0;
++                              gdm_config = soc->reg_map->gdma_to_ppe[0];
++                      }
++                      mtk_gdm_config(eth, target_mac->id, gdm_config);
++              }
++              /* Reset and enable PSE */
++              mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
++              mtk_w32(eth, 0, MTK_RST_GL);
+               napi_enable(&eth->tx_napi);
+               napi_enable(&eth->rx_napi);
+               mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+               mtk_rx_irq_enable(eth, soc->rx.irq_done_mask);
+               refcount_set(&eth->dma_refcnt, 1);
+-      }
+-      else
++      } else {
+               refcount_inc(&eth->dma_refcnt);
++      }
+       phylink_start(mac->phylink);
+       netif_tx_start_all_queues(dev);
+@@ -3478,7 +3502,8 @@ static int mtk_stop(struct net_device *d
+       if (!refcount_dec_and_test(&eth->dma_refcnt))
+               return 0;
+-      mtk_gdm_config(eth, MTK_GDMA_DROP_ALL);
++      for (i = 0; i < MTK_MAX_DEVS; i++)
++              mtk_gdm_config(eth, i, MTK_GDMA_DROP_ALL);
+       mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+       mtk_rx_irq_disable(eth, eth->soc->rx.irq_done_mask);
+@@ -4957,23 +4982,24 @@ static int mtk_probe(struct platform_dev
+       }
+       if (eth->soc->offload_version) {
+-              u32 num_ppe = mtk_is_netsys_v2_or_greater(eth) ? 2 : 1;
++              u8 ppe_num = eth->soc->ppe_num;
+-              num_ppe = min_t(u32, ARRAY_SIZE(eth->ppe), num_ppe);
+-              for (i = 0; i < num_ppe; i++) {
+-                      u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400;
++              ppe_num = min_t(u8, ARRAY_SIZE(eth->ppe), ppe_num);
++              for (i = 0; i < ppe_num; i++) {
++                      u32 ppe_addr = eth->soc->reg_map->ppe_base;
++                      ppe_addr += (i == 2 ? 0xc00 : i * 0x400);
+                       eth->ppe[i] = mtk_ppe_init(eth, eth->base + ppe_addr, i);
+                       if (!eth->ppe[i]) {
+                               err = -ENOMEM;
+                               goto err_deinit_ppe;
+                       }
+-              }
++                      err = mtk_eth_offload_init(eth, i);
+-              err = mtk_eth_offload_init(eth);
+-              if (err)
+-                      goto err_deinit_ppe;
++                      if (err)
++                              goto err_deinit_ppe;
++              }
+       }
+       for (i = 0; i < MTK_MAX_DEVS; i++) {
+@@ -5076,6 +5102,7 @@ static const struct mtk_soc_data mt7621_
+       .required_pctl = false,
+       .version = 1,
+       .offload_version = 1,
++      .ppe_num = 1,
+       .hash_offset = 2,
+       .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
+       .tx = {
+@@ -5104,6 +5131,7 @@ static const struct mtk_soc_data mt7622_
+       .required_pctl = false,
+       .version = 1,
+       .offload_version = 2,
++      .ppe_num = 1,
+       .hash_offset = 2,
+       .has_accounting = true,
+       .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
+@@ -5132,6 +5160,7 @@ static const struct mtk_soc_data mt7623_
+       .required_pctl = true,
+       .version = 1,
+       .offload_version = 1,
++      .ppe_num = 1,
+       .hash_offset = 2,
+       .foe_entry_size = MTK_FOE_ENTRY_V1_SIZE,
+       .disable_pll_modes = true,
+@@ -5187,6 +5216,7 @@ static const struct mtk_soc_data mt7981_
+       .required_pctl = false,
+       .version = 2,
+       .offload_version = 2,
++      .ppe_num = 2,
+       .hash_offset = 4,
+       .has_accounting = true,
+       .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
+@@ -5216,6 +5246,7 @@ static const struct mtk_soc_data mt7986_
+       .required_pctl = false,
+       .version = 2,
+       .offload_version = 2,
++      .ppe_num = 2,
+       .hash_offset = 4,
+       .has_accounting = true,
+       .foe_entry_size = MTK_FOE_ENTRY_V2_SIZE,
+@@ -5245,6 +5276,7 @@ static const struct mtk_soc_data mt7988_
+       .required_pctl = false,
+       .version = 3,
+       .offload_version = 2,
++      .ppe_num = 3,
+       .hash_offset = 4,
+       .has_accounting = true,
+       .foe_entry_size = MTK_FOE_ENTRY_V3_SIZE,
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1132,7 +1132,7 @@ struct mtk_reg_map {
+               u32     tx_sch_rate;    /* tx scheduler rate control registers */
+       } qdma;
+       u32     gdm1_cnt;
+-      u32     gdma_to_ppe;
++      u32     gdma_to_ppe[3];
+       u32     ppe_base;
+       u32     wdma_base[3];
+       u32     pse_iq_sta;
+@@ -1170,6 +1170,7 @@ struct mtk_soc_data {
+       u8              offload_version;
+       u8              hash_offset;
+       u8              version;
++      u8              ppe_num;
+       u16             foe_entry_size;
+       netdev_features_t hw_features;
+       bool            has_accounting;
+@@ -1294,7 +1295,7 @@ struct mtk_eth {
+       struct metadata_dst             *dsa_meta[MTK_MAX_DSA_PORTS];
+-      struct mtk_ppe                  *ppe[2];
++      struct mtk_ppe                  *ppe[3];
+       struct rhashtable               flow_table;
+       struct bpf_prog                 __rcu *prog;
+@@ -1319,6 +1320,7 @@ struct mtk_eth {
+ struct mtk_mac {
+       int                             id;
+       phy_interface_t                 interface;
++      u8                              ppe_idx;
+       int                             speed;
+       struct device_node              *of_node;
+       struct phylink                  *phylink;
+@@ -1440,7 +1442,7 @@ int mtk_gmac_sgmii_path_setup(struct mtk
+ int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
+-int mtk_eth_offload_init(struct mtk_eth *eth);
++int mtk_eth_offload_init(struct mtk_eth *eth, u8 id);
+ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
+                    void *type_data);
+ int mtk_flow_offload_cmd(struct mtk_eth *eth, struct flow_cls_offload *cls,
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -245,10 +245,10 @@ mtk_flow_offload_replace(struct mtk_eth
+                        int ppe_index)
+ {
+       struct flow_rule *rule = flow_cls_offload_flow_rule(f);
++      struct net_device *idev = NULL, *odev = NULL;
+       struct flow_action_entry *act;
+       struct mtk_flow_data data = {};
+       struct mtk_foe_entry foe;
+-      struct net_device *odev = NULL;
+       struct mtk_flow_entry *entry;
+       int offload_type = 0;
+       int wed_index = -1;
+@@ -264,6 +264,17 @@ mtk_flow_offload_replace(struct mtk_eth
+               struct flow_match_meta match;
+               flow_rule_match_meta(rule, &match);
++              if (mtk_is_netsys_v2_or_greater(eth)) {
++                      idev = __dev_get_by_index(&init_net, match.key->ingress_ifindex);
++                      if (idev) {
++                              struct mtk_mac *mac = netdev_priv(idev);
++
++                              if (WARN_ON(mac->ppe_idx >= eth->soc->ppe_num))
++                                      return -EINVAL;
++
++                              ppe_index = mac->ppe_idx;
++                      }
++              }
+       } else {
+               return -EOPNOTSUPP;
+       }
+@@ -630,7 +641,9 @@ int mtk_eth_setup_tc(struct net_device *
+       }
+ }
+-int mtk_eth_offload_init(struct mtk_eth *eth)
++int mtk_eth_offload_init(struct mtk_eth *eth, u8 id)
+ {
++      if (!eth->ppe[id] || !eth->ppe[id]->foe_table)
++              return 0;
+       return rhashtable_init(&eth->flow_table, &mtk_flow_ht_params);
+ }
diff --git a/target/linux/generic/backport-6.6/752-27-v6.10-net-ethernet-mtk_eth_soc-ppe-prevent-ppe-update-for-.patch b/target/linux/generic/backport-6.6/752-27-v6.10-net-ethernet-mtk_eth_soc-ppe-prevent-ppe-update-for-.patch
new file mode 100644 (file)
index 0000000..dbf574d
--- /dev/null
@@ -0,0 +1,30 @@
+From 73cfd947dbdb25ef9863ac49c4596a7d53ad4025 Mon Sep 17 00:00:00 2001
+From: Elad Yifee <eladwf@gmail.com>
+Date: Sun, 23 Jun 2024 20:51:09 +0300
+Subject: [PATCH] net: ethernet: mtk_eth_soc: ppe: prevent ppe update for
+ non-mtk devices
+
+Introduce an additional validation to ensure that the PPE index
+is modified exclusively for mtk_eth ingress devices.
+This primarily addresses the issue related
+to WED operation with multiple PPEs.
+
+Fixes: dee4dd10c79a ("net: ethernet: mtk_eth_soc: ppe: add support for multiple PPEs")
+Signed-off-by: Elad Yifee <eladwf@gmail.com>
+Link: https://lore.kernel.org/r/20240623175113.24437-1-eladwf@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -266,7 +266,7 @@ mtk_flow_offload_replace(struct mtk_eth
+               flow_rule_match_meta(rule, &match);
+               if (mtk_is_netsys_v2_or_greater(eth)) {
+                       idev = __dev_get_by_index(&init_net, match.key->ingress_ifindex);
+-                      if (idev) {
++                      if (idev && idev->netdev_ops == eth->netdev[0]->netdev_ops) {
+                               struct mtk_mac *mac = netdev_priv(idev);
+                               if (WARN_ON(mac->ppe_idx >= eth->soc->ppe_num))
diff --git a/target/linux/generic/backport-6.6/752-28-v6.10-net-ethernet-mediatek-Allow-gaps-in-MAC-allocation.patch b/target/linux/generic/backport-6.6/752-28-v6.10-net-ethernet-mediatek-Allow-gaps-in-MAC-allocation.patch
new file mode 100644 (file)
index 0000000..b62586b
--- /dev/null
@@ -0,0 +1,32 @@
+From 3b2aef99221d395ce37efa426d7b50e7dcd621d6 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 1 Jul 2024 20:28:14 +0100
+Subject: [PATCH] net: ethernet: mediatek: Allow gaps in MAC allocation
+
+Some devices with MediaTek SoCs don't use the first but only the second
+MAC in the chip. Especially with MT7981 which got a built-in 1GE PHY
+connected to the second MAC this is quite common.
+Make sure to reset and enable PSE also in those cases by skipping gaps
+using 'continue' instead of aborting the loop using 'break'.
+
+Fixes: dee4dd10c79a ("net: ethernet: mtk_eth_soc: ppe: add support for multiple PPEs")
+Suggested-by: Elad Yifee <eladwf@gmail.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Link: https://patch.msgid.link/379ae584cea112db60f4ada79c7e5ba4f3364a64.1719862038.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3396,7 +3396,7 @@ static int mtk_open(struct net_device *d
+               for (i = 0; i < MTK_MAX_DEVS; i++) {
+                       if (!eth->netdev[i])
+-                              break;
++                              continue;
+                       target_mac = netdev_priv(eth->netdev[i]);
+                       if (!soc->offload_version) {
diff --git a/target/linux/generic/backport-6.6/752-29-v6.10-net-ethernet-mtk_ppe-Change-PPE-entries-number-to-16.patch b/target/linux/generic/backport-6.6/752-29-v6.10-net-ethernet-mtk_ppe-Change-PPE-entries-number-to-16.patch
new file mode 100644 (file)
index 0000000..c6b332f
--- /dev/null
@@ -0,0 +1,29 @@
+From ca18300e00d584d5693127eb60c108b84883b8ac Mon Sep 17 00:00:00 2001
+From: Shengyu Qu <wiagn233@outlook.com>
+Date: Fri, 5 Jul 2024 01:26:26 +0800
+Subject: [PATCH] net: ethernet: mtk_ppe: Change PPE entries number to 16K
+
+MT7981,7986 and 7988 all supports 32768 PPE entries, and MT7621/MT7620
+supports 16384 PPE entries, but only set to 8192 entries in driver. So
+incrase max entries to 16384 instead.
+
+Signed-off-by: Elad Yifee <eladwf@gmail.com>
+Signed-off-by: Shengyu Qu <wiagn233@outlook.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://patch.msgid.link/TY3P286MB261103F937DE4EEB0F88437D98DE2@TY3P286MB2611.JPNP286.PROD.OUTLOOK.COM
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_ppe.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -8,7 +8,7 @@
+ #include <linux/bitfield.h>
+ #include <linux/rhashtable.h>
+-#define MTK_PPE_ENTRIES_SHIFT         3
++#define MTK_PPE_ENTRIES_SHIFT         4
+ #define MTK_PPE_ENTRIES                       (1024 << MTK_PPE_ENTRIES_SHIFT)
+ #define MTK_PPE_HASH_MASK             (MTK_PPE_ENTRIES - 1)
+ #define MTK_PPE_WAIT_TIMEOUT_US               1000000
diff --git a/target/linux/generic/backport-6.6/752-30-v6.10-net-ethernet-mtk_eth_soc-implement-.-get-set-_pausep.patch b/target/linux/generic/backport-6.6/752-30-v6.10-net-ethernet-mtk_eth_soc-implement-.-get-set-_pausep.patch
new file mode 100644 (file)
index 0000000..09d7054
--- /dev/null
@@ -0,0 +1,55 @@
+From 064fbc4e9b5a6dbda7fe7b67dc7e9e95d31f8d75 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 4 Jul 2024 11:14:55 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: implement .{get,set}_pauseparam
+ ethtool ops
+
+Implement operations to get and set flow-control link parameters.
+Both is done by simply calling phylink_ethtool_{get,set}_pauseparam().
+Fix whitespace in mtk_ethtool_ops while at it.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Michal Kubiak <michal.kubiak@intel.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Tested-by: Rui Salvaterra <rsalvaterra@gmail.com>
+Link: https://patch.msgid.link/e3ece47323444631d6cb479f32af0dfd6d145be0.1720088047.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4462,6 +4462,20 @@ static int mtk_set_rxnfc(struct net_devi
+       return ret;
+ }
++static void mtk_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
++{
++      struct mtk_mac *mac = netdev_priv(dev);
++
++      phylink_ethtool_get_pauseparam(mac->phylink, pause);
++}
++
++static int mtk_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause)
++{
++      struct mtk_mac *mac = netdev_priv(dev);
++
++      return phylink_ethtool_set_pauseparam(mac->phylink, pause);
++}
++
+ static u16 mtk_select_queue(struct net_device *dev, struct sk_buff *skb,
+                           struct net_device *sb_dev)
+ {
+@@ -4490,8 +4504,10 @@ static const struct ethtool_ops mtk_etht
+       .get_strings            = mtk_get_strings,
+       .get_sset_count         = mtk_get_sset_count,
+       .get_ethtool_stats      = mtk_get_ethtool_stats,
++      .get_pauseparam         = mtk_get_pauseparam,
++      .set_pauseparam         = mtk_set_pauseparam,
+       .get_rxnfc              = mtk_get_rxnfc,
+-      .set_rxnfc              = mtk_set_rxnfc,
++      .set_rxnfc              = mtk_set_rxnfc,
+ };
+ static const struct net_device_ops mtk_netdev_ops = {
diff --git a/target/linux/generic/backport-6.6/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch b/target/linux/generic/backport-6.6/760-v6.9-net-phy-aquantia-add-AQR111-and-AQR111B0-PHY-ID.patch
new file mode 100644 (file)
index 0000000..4190a3d
--- /dev/null
@@ -0,0 +1,102 @@
+From 038ba1dc4e54d51d953f5618d8eb5dd39bd9de25 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 13 Feb 2024 14:35:51 +0100
+Subject: [PATCH] net: phy: aquantia: add AQR111 and AQR111B0 PHY ID
+
+Add Aquantia AQR111 and AQR111B0 PHY ID. These PHY advertise 10G speed
+but actually supports up to 5G speed, hence some manual fixup is needed.
+
+The Aquantia AQR111B0 PHY is just a variant of the AQR111 with smaller
+chip size.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240213133558.1836-1-ansuelsmth@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/aquantia/aquantia_main.c | 52 ++++++++++++++++++++++++
+ 1 file changed, 52 insertions(+)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -22,6 +22,8 @@
+ #define PHY_ID_AQR107 0x03a1b4e0
+ #define PHY_ID_AQCS109        0x03a1b5c2
+ #define PHY_ID_AQR405 0x03a1b4b0
++#define PHY_ID_AQR111 0x03a1b610
++#define PHY_ID_AQR111B0       0x03a1b612
+ #define PHY_ID_AQR112 0x03a1b662
+ #define PHY_ID_AQR412 0x03a1b712
+ #define PHY_ID_AQR113C        0x31c31c12
+@@ -672,6 +674,16 @@ static int aqr107_probe(struct phy_devic
+       return aqr_hwmon_probe(phydev);
+ }
++static int aqr111_config_init(struct phy_device *phydev)
++{
++      /* AQR111 reports supporting speed up to 10G,
++       * however only speeds up to 5G are supported.
++       */
++      phy_set_max_speed(phydev, SPEED_5000);
++
++      return aqr107_config_init(phydev);
++}
++
+ static struct phy_driver aqr_driver[] = {
+ {
+       PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
+@@ -746,6 +758,44 @@ static struct phy_driver aqr_driver[] =
+       .link_change_notify = aqr107_link_change_notify,
+ },
+ {
++      PHY_ID_MATCH_MODEL(PHY_ID_AQR111),
++      .name           = "Aquantia AQR111",
++      .probe          = aqr107_probe,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .config_init    = aqr111_config_init,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr107_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_AQR111B0),
++      .name           = "Aquantia AQR111B0",
++      .probe          = aqr107_probe,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .config_init    = aqr111_config_init,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr107_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_AQR405),
+       .name           = "Aquantia AQR405",
+       .config_aneg    = aqr_config_aneg,
+@@ -820,6 +870,8 @@ static struct mdio_device_id __maybe_unu
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR111) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
diff --git a/target/linux/generic/backport-6.6/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch b/target/linux/generic/backport-6.6/761-v6.9-net-phy-aquantia-add-AQR113-PHY-ID.patch
new file mode 100644 (file)
index 0000000..cd9161d
--- /dev/null
@@ -0,0 +1,60 @@
+From 71b605d32017e5b8d257db7344bc2f8e8fcc973e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 15 Feb 2024 16:30:05 +0100
+Subject: [PATCH] net: phy: aquantia: add AQR113 PHY ID
+
+Add Aquantia AQR113 PHY ID. Aquantia AQR113 is just a chip size variant of
+the already supported AQR133C where the only difference is the PHY ID
+and the hw chip size.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -26,6 +26,7 @@
+ #define PHY_ID_AQR111B0       0x03a1b612
+ #define PHY_ID_AQR112 0x03a1b662
+ #define PHY_ID_AQR412 0x03a1b712
++#define PHY_ID_AQR113 0x31c31c40
+ #define PHY_ID_AQR113C        0x31c31c12
+ #define MDIO_PHYXS_VEND_IF_STATUS             0xe812
+@@ -840,6 +841,25 @@ static struct phy_driver aqr_driver[] =
+       .link_change_notify = aqr107_link_change_notify,
+ },
+ {
++      PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
++      .name           = "Aquantia AQR113",
++      .probe          = aqr107_probe,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .config_init    = aqr107_config_init,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt       = aqr_handle_interrupt,
++      .read_status    = aqr107_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_AQR113C),
+       .name           = "Aquantia AQR113C",
+       .probe          = aqr107_probe,
+@@ -874,6 +894,7 @@ static struct mdio_device_id __maybe_unu
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR112) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+       { }
+ };
diff --git a/target/linux/generic/backport-6.6/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch b/target/linux/generic/backport-6.6/762-v6.9-net-phy-aquantia-add-AQR813-PHY-ID.patch
new file mode 100644 (file)
index 0000000..fbdf810
--- /dev/null
@@ -0,0 +1,59 @@
+From 6d47302a3f0ba31445478d518d98bd55918bc8ab Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 15 Feb 2024 22:43:30 +0100
+Subject: [PATCH] net: phy: aquantia: add AQR813 PHY ID
+
+Aquantia AQR813 is the Octal Port variant of the AQR113. Add PHY ID for
+it to provide support for it.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -28,6 +28,7 @@
+ #define PHY_ID_AQR412 0x03a1b712
+ #define PHY_ID_AQR113 0x31c31c40
+ #define PHY_ID_AQR113C        0x31c31c12
++#define PHY_ID_AQR813 0x31c31cb2
+ #define MDIO_PHYXS_VEND_IF_STATUS             0xe812
+ #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK   GENMASK(7, 3)
+@@ -878,6 +879,25 @@ static struct phy_driver aqr_driver[] =
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
+ },
++{
++      PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
++      .name           = "Aquantia AQR813",
++      .probe          = aqr107_probe,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .config_init    = aqr107_config_init,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr107_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);
+@@ -896,6 +916,7 @@ static struct mdio_device_id __maybe_unu
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
+       { }
+ };
diff --git a/target/linux/generic/backport-6.6/763-v6.10-net-dsa-introduce-dsa_phylink_to_port.patch b/target/linux/generic/backport-6.6/763-v6.10-net-dsa-introduce-dsa_phylink_to_port.patch
new file mode 100644 (file)
index 0000000..0e7ace9
--- /dev/null
@@ -0,0 +1,90 @@
+From f13b2b33c7674fa0988dfaa9adb95d7d912b489f Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 10 Apr 2024 20:42:38 +0100
+Subject: [PATCH 1/2] net: dsa: introduce dsa_phylink_to_port()
+
+We convert from a phylink_config struct to a dsa_port struct in many
+places, let's provide a helper for this.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/E1rudqA-006K9B-85@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ include/net/dsa.h |  6 ++++++
+ net/dsa/port.c    | 12 ++++++------
+ 2 files changed, 12 insertions(+), 6 deletions(-)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -327,6 +327,12 @@ struct dsa_port {
+       };
+ };
++static inline struct dsa_port *
++dsa_phylink_to_port(struct phylink_config *config)
++{
++      return container_of(config, struct dsa_port, pl_config);
++}
++
+ /* TODO: ideally DSA ports would have a single dp->link_dp member,
+  * and no dst->rtable nor this struct dsa_link would be needed,
+  * but this would require some more complex tree walking,
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1572,7 +1572,7 @@ static struct phylink_pcs *
+ dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
+                               phy_interface_t interface)
+ {
+-      struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
++      struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
+       struct dsa_switch *ds = dp->ds;
+@@ -1586,7 +1586,7 @@ static int dsa_port_phylink_mac_prepare(
+                                       unsigned int mode,
+                                       phy_interface_t interface)
+ {
+-      struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
++      struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct dsa_switch *ds = dp->ds;
+       int err = 0;
+@@ -1601,7 +1601,7 @@ static void dsa_port_phylink_mac_config(
+                                       unsigned int mode,
+                                       const struct phylink_link_state *state)
+ {
+-      struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
++      struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct dsa_switch *ds = dp->ds;
+       if (!ds->ops->phylink_mac_config)
+@@ -1614,7 +1614,7 @@ static int dsa_port_phylink_mac_finish(s
+                                      unsigned int mode,
+                                      phy_interface_t interface)
+ {
+-      struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
++      struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct dsa_switch *ds = dp->ds;
+       int err = 0;
+@@ -1629,7 +1629,7 @@ static void dsa_port_phylink_mac_link_do
+                                          unsigned int mode,
+                                          phy_interface_t interface)
+ {
+-      struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
++      struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct phy_device *phydev = NULL;
+       struct dsa_switch *ds = dp->ds;
+@@ -1652,7 +1652,7 @@ static void dsa_port_phylink_mac_link_up
+                                        int speed, int duplex,
+                                        bool tx_pause, bool rx_pause)
+ {
+-      struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
++      struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct dsa_switch *ds = dp->ds;
+       if (!ds->ops->phylink_mac_link_up) {
diff --git a/target/linux/generic/backport-6.6/764-v6.10-net-dsa-allow-DSA-switch-drivers-to-provide-their-ow.patch b/target/linux/generic/backport-6.6/764-v6.10-net-dsa-allow-DSA-switch-drivers-to-provide-their-ow.patch
new file mode 100644 (file)
index 0000000..7c9ab16
--- /dev/null
@@ -0,0 +1,119 @@
+From c22d8240fcd73a1c3ec8dcb055bd583fb970c375 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 10 Apr 2024 20:42:43 +0100
+Subject: [PATCH 2/2] net: dsa: allow DSA switch drivers to provide their own
+ phylink mac ops
+
+Rather than having a shim for each and every phylink MAC operation,
+allow DSA switch drivers to provide their own ops structure. When a
+DSA driver provides the phylink MAC operations, the shimmed ops must
+not be provided, so fail an attempt to register a switch with both
+the phylink_mac_ops in struct dsa_switch and the phylink_mac_*
+operations populated in dsa_switch_ops populated.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Link: https://lore.kernel.org/r/E1rudqF-006K9H-Cc@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ include/net/dsa.h |  5 +++++
+ net/dsa/dsa.c     | 11 +++++++++++
+ net/dsa/port.c    | 26 ++++++++++++++++++++------
+ 3 files changed, 36 insertions(+), 6 deletions(-)
+
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -458,6 +458,11 @@ struct dsa_switch {
+       const struct dsa_switch_ops     *ops;
+       /*
++       * Allow a DSA switch driver to override the phylink MAC ops
++       */
++      const struct phylink_mac_ops    *phylink_mac_ops;
++
++      /*
+        * Slave mii_bus and devices for the individual ports.
+        */
+       u32                     phys_mii_mask;
+--- a/net/dsa/dsa.c
++++ b/net/dsa/dsa.c
+@@ -1510,6 +1510,17 @@ static int dsa_switch_probe(struct dsa_s
+       if (!ds->num_ports)
+               return -EINVAL;
++      if (ds->phylink_mac_ops) {
++              if (ds->ops->phylink_mac_select_pcs ||
++                  ds->ops->phylink_mac_prepare ||
++                  ds->ops->phylink_mac_config ||
++                  ds->ops->phylink_mac_finish ||
++                  ds->ops->phylink_mac_link_down ||
++                  ds->ops->phylink_mac_link_up ||
++                  ds->ops->adjust_link)
++                      return -EINVAL;
++      }
++
+       if (np) {
+               err = dsa_switch_parse_of(ds, np);
+               if (err)
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -1677,6 +1677,7 @@ static const struct phylink_mac_ops dsa_
+ int dsa_port_phylink_create(struct dsa_port *dp)
+ {
++      const struct phylink_mac_ops *mac_ops;
+       struct dsa_switch *ds = dp->ds;
+       phy_interface_t mode;
+       struct phylink *pl;
+@@ -1700,8 +1701,12 @@ int dsa_port_phylink_create(struct dsa_p
+               }
+       }
+-      pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
+-                          mode, &dsa_port_phylink_mac_ops);
++      mac_ops = &dsa_port_phylink_mac_ops;
++      if (ds->phylink_mac_ops)
++              mac_ops = ds->phylink_mac_ops;
++
++      pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), mode,
++                          mac_ops);
+       if (IS_ERR(pl)) {
+               pr_err("error creating PHYLINK: %ld\n", PTR_ERR(pl));
+               return PTR_ERR(pl);
+@@ -1967,12 +1972,23 @@ static void dsa_shared_port_validate_of(
+               dn, dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index);
+ }
++static void dsa_shared_port_link_down(struct dsa_port *dp)
++{
++      struct dsa_switch *ds = dp->ds;
++
++      if (ds->phylink_mac_ops && ds->phylink_mac_ops->mac_link_down)
++              ds->phylink_mac_ops->mac_link_down(&dp->pl_config, MLO_AN_FIXED,
++                                                 PHY_INTERFACE_MODE_NA);
++      else if (ds->ops->phylink_mac_link_down)
++              ds->ops->phylink_mac_link_down(ds, dp->index, MLO_AN_FIXED,
++                                             PHY_INTERFACE_MODE_NA);
++}
++
+ int dsa_shared_port_link_register_of(struct dsa_port *dp)
+ {
+       struct dsa_switch *ds = dp->ds;
+       bool missing_link_description;
+       bool missing_phy_mode;
+-      int port = dp->index;
+       dsa_shared_port_validate_of(dp, &missing_phy_mode,
+                                   &missing_link_description);
+@@ -1988,9 +2004,7 @@ int dsa_shared_port_link_register_of(str
+                                "Skipping phylink registration for %s port %d\n",
+                                dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index);
+               } else {
+-                      if (ds->ops->phylink_mac_link_down)
+-                              ds->ops->phylink_mac_link_down(ds, port,
+-                                      MLO_AN_FIXED, PHY_INTERFACE_MODE_NA);
++                      dsa_shared_port_link_down(dp);
+                       return dsa_shared_port_phylink_register(dp);
+               }
diff --git a/target/linux/generic/backport-6.6/765-v6.9-net-phy-aquantia-add-support-for-AQR114C-PHY-ID.patch b/target/linux/generic/backport-6.6/765-v6.9-net-phy-aquantia-add-support-for-AQR114C-PHY-ID.patch
new file mode 100644 (file)
index 0000000..714ef49
--- /dev/null
@@ -0,0 +1,69 @@
+From c278ec644377249aba5b1e1ca2b5705fd1c0132c Mon Sep 17 00:00:00 2001
+From: Paweł Owoc <frut3k7@gmail.com>
+Date: Mon, 1 Apr 2024 16:51:06 +0200
+Subject: [PATCH net-next v2] net: phy: aquantia: add support for AQR114C PHY ID  
+
+Add support for AQR114C PHY ID. This PHY advertise 10G speed:
+SPEED(0x04): 0x6031
+  capabilities: -400g +5g +2.5g -200g -25g -10g-xr -100g -40g -10g/1g -10
+                +100 +1000 -10-ts -2-tl +10g
+EXTABLE(0x0B): 0x40fc
+  capabilities: -10g-cx4 -10g-lrm +10g-t +10g-kx4 +10g-kr +1000-t +1000-kx
+                +100-tx -10-t -p2mp -40g/100g -1000/100-t1 -25g -200g/400g
+                +2.5g/5g -1000-h
+
+but supports only up to 5G speed (as with AQR111/111B0).
+AQR111 init config is used to set max speed 5G.
+
+Signed-off-by: Paweł Owoc <frut3k7@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240401145114.1699451-1-frut3k7@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/aquantia/aquantia_main.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -28,6 +28,7 @@
+ #define PHY_ID_AQR412 0x03a1b712
+ #define PHY_ID_AQR113 0x31c31c40
+ #define PHY_ID_AQR113C        0x31c31c12
++#define PHY_ID_AQR114C        0x31c31c22
+ #define PHY_ID_AQR813 0x31c31cb2
+ #define MDIO_PHYXS_VEND_IF_STATUS             0xe812
+@@ -880,6 +881,25 @@ static struct phy_driver aqr_driver[] =
+       .link_change_notify = aqr107_link_change_notify,
+ },
+ {
++      PHY_ID_MATCH_MODEL(PHY_ID_AQR114C),
++      .name           = "Aquantia AQR114C",
++      .probe          = aqr107_probe,
++      .get_rate_matching = aqr107_get_rate_matching,
++      .config_init    = aqr111_config_init,
++      .config_aneg    = aqr_config_aneg,
++      .config_intr    = aqr_config_intr,
++      .handle_interrupt = aqr_handle_interrupt,
++      .read_status    = aqr107_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_AQR813),
+       .name           = "Aquantia AQR813",
+       .probe          = aqr107_probe,
+@@ -916,6 +936,7 @@ static struct mdio_device_id __maybe_unu
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR412) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR113) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR114C) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
+       { }
+ };
diff --git a/target/linux/generic/backport-6.6/770-net-introduce-napi_is_scheduled-helper.patch b/target/linux/generic/backport-6.6/770-net-introduce-napi_is_scheduled-helper.patch
new file mode 100644 (file)
index 0000000..d7b2b27
--- /dev/null
@@ -0,0 +1,96 @@
+From 7f3eb2174512fe6c9c0f062e96eccb0d3cc6d5cd Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 18 Oct 2023 14:35:47 +0200
+Subject: [PATCH] net: introduce napi_is_scheduled helper
+
+We currently have napi_if_scheduled_mark_missed that can be used to
+check if napi is scheduled but that does more thing than simply checking
+it and return a bool. Some driver already implement custom function to
+check if napi is scheduled.
+
+Drop these custom function and introduce napi_is_scheduled that simply
+check if napi is scheduled atomically.
+
+Update any driver and code that implement a similar check and instead
+use this new helper.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/chelsio/cxgb3/sge.c  |  8 --------
+ drivers/net/wireless/realtek/rtw89/core.c |  2 +-
+ include/linux/netdevice.h                 | 23 +++++++++++++++++++++++
+ net/core/dev.c                            |  2 +-
+ 4 files changed, 25 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/ethernet/chelsio/cxgb3/sge.c
++++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c
+@@ -2501,14 +2501,6 @@ static int napi_rx_handler(struct napi_s
+       return work_done;
+ }
+-/*
+- * Returns true if the device is already scheduled for polling.
+- */
+-static inline int napi_is_scheduled(struct napi_struct *napi)
+-{
+-      return test_bit(NAPI_STATE_SCHED, &napi->state);
+-}
+-
+ /**
+  *    process_pure_responses - process pure responses from a response queue
+  *    @adap: the adapter
+--- a/drivers/net/wireless/realtek/rtw89/core.c
++++ b/drivers/net/wireless/realtek/rtw89/core.c
+@@ -1744,7 +1744,7 @@ static void rtw89_core_rx_to_mac80211(st
+       struct napi_struct *napi = &rtwdev->napi;
+       /* In low power mode, napi isn't scheduled. Receive it to netif. */
+-      if (unlikely(!test_bit(NAPI_STATE_SCHED, &napi->state)))
++      if (unlikely(!napi_is_scheduled(napi)))
+               napi = NULL;
+       rtw89_core_hw_to_sband_rate(rx_status);
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -480,6 +480,29 @@ static inline bool napi_prefer_busy_poll
+       return test_bit(NAPI_STATE_PREFER_BUSY_POLL, &n->state);
+ }
++/**
++ * napi_is_scheduled - test if NAPI is scheduled
++ * @n: NAPI context
++ *
++ * This check is "best-effort". With no locking implemented,
++ * a NAPI can be scheduled or terminate right after this check
++ * and produce not precise results.
++ *
++ * NAPI_STATE_SCHED is an internal state, napi_is_scheduled
++ * should not be used normally and napi_schedule should be
++ * used instead.
++ *
++ * Use only if the driver really needs to check if a NAPI
++ * is scheduled for example in the context of delayed timer
++ * that can be skipped if a NAPI is already scheduled.
++ *
++ * Return True if NAPI is scheduled, False otherwise.
++ */
++static inline bool napi_is_scheduled(struct napi_struct *n)
++{
++      return test_bit(NAPI_STATE_SCHED, &n->state);
++}
++
+ bool napi_schedule_prep(struct napi_struct *n);
+ /**
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -6610,7 +6610,7 @@ static int __napi_poll(struct napi_struc
+        * accidentally calling ->poll() when NAPI is not scheduled.
+        */
+       work = 0;
+-      if (test_bit(NAPI_STATE_SCHED, &n->state)) {
++      if (napi_is_scheduled(n)) {
+               work = n->poll(n, weight);
+               trace_napi_poll(n, work, weight);
+       }
diff --git a/target/linux/generic/backport-6.6/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch b/target/linux/generic/backport-6.6/771-v6.7-01-net-stmmac-improve-TX-timer-arm-logic.patch
new file mode 100644 (file)
index 0000000..a7da409
--- /dev/null
@@ -0,0 +1,77 @@
+From 2d1a42cf7f77cda54dbbee18d00b1200e7bc22aa Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 18 Oct 2023 14:35:48 +0200
+Subject: [PATCH 1/3] net: stmmac: improve TX timer arm logic
+
+There is currently a problem with the TX timer getting armed multiple
+unnecessary times causing big performance regression on some device that
+suffer from heavy handling of hrtimer rearm.
+
+The use of the TX timer is an old implementation that predates the napi
+implementation and the interrupt enable/disable handling.
+
+Due to stmmac being a very old code, the TX timer was never evaluated
+again with this new implementation and was kept there causing
+performance regression. The performance regression started to appear
+with kernel version 4.19 with 8fce33317023 ("net: stmmac: Rework coalesce
+timer and fix multi-queue races") where the timer was reduced to 1ms
+causing it to be armed 40 times more than before.
+
+Decreasing the timer made the problem more present and caused the
+regression in the other of 600-700mbps on some device (regression where
+this was notice is ipq806x).
+
+The problem is in the fact that handling the hrtimer on some target is
+expensive and recent kernel made the timer armed much more times.
+A solution that was proposed was reverting the hrtimer change and use
+mod_timer but such solution would still hide the real problem in the
+current implementation.
+
+To fix the regression, apply some additional logic and skip arming the
+timer when not needed.
+
+Arm the timer ONLY if a napi is not already scheduled. Running the timer
+is redundant since the same function (stmmac_tx_clean) will run in the
+napi TX poll. Also try to cancel any timer if a napi is scheduled to
+prevent redundant run of TX call.
+
+With the following new logic the original performance are restored while
+keeping using the hrtimer.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ .../net/ethernet/stmicro/stmmac/stmmac_main.c  | 18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -2988,13 +2988,25 @@ static void stmmac_tx_timer_arm(struct s
+ {
+       struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+       u32 tx_coal_timer = priv->tx_coal_timer[queue];
++      struct stmmac_channel *ch;
++      struct napi_struct *napi;
+       if (!tx_coal_timer)
+               return;
+-      hrtimer_start(&tx_q->txtimer,
+-                    STMMAC_COAL_TIMER(tx_coal_timer),
+-                    HRTIMER_MODE_REL);
++      ch = &priv->channel[tx_q->queue_index];
++      napi = tx_q->xsk_pool ? &ch->rxtx_napi : &ch->tx_napi;
++
++      /* Arm timer only if napi is not already scheduled.
++       * Try to cancel any timer if napi is scheduled, timer will be armed
++       * again in the next scheduled napi.
++       */
++      if (unlikely(!napi_is_scheduled(napi)))
++              hrtimer_start(&tx_q->txtimer,
++                            STMMAC_COAL_TIMER(tx_coal_timer),
++                            HRTIMER_MODE_REL);
++      else
++              hrtimer_try_to_cancel(&tx_q->txtimer);
+ }
+ /**
diff --git a/target/linux/generic/backport-6.6/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch b/target/linux/generic/backport-6.6/771-v6.7-02-net-stmmac-move-TX-timer-arm-after-DMA-enable.patch
new file mode 100644 (file)
index 0000000..bd61343
--- /dev/null
@@ -0,0 +1,100 @@
+From a594166387fe08e6f5a32130c400249a35b298f9 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 18 Oct 2023 14:35:49 +0200
+Subject: [PATCH 2/3] net: stmmac: move TX timer arm after DMA enable
+
+Move TX timer arm call after DMA interrupt is enabled again.
+
+The TX timer arm function changed logic and now is skipped if a napi is
+already scheduled. By moving the TX timer arm call after DMA is enabled,
+we permit to correctly skip if a DMA interrupt has been fired and a napi
+has been scheduled again.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ .../net/ethernet/stmicro/stmmac/stmmac_main.c | 22 +++++++++++++++----
+ 1 file changed, 18 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -2536,9 +2536,13 @@ static void stmmac_bump_dma_threshold(st
+  * @priv: driver private structure
+  * @budget: napi budget limiting this functions packet handling
+  * @queue: TX queue index
++ * @pending_packets: signal to arm the TX coal timer
+  * Description: it reclaims the transmit resources after transmission completes.
++ * If some packets still needs to be handled, due to TX coalesce, set
++ * pending_packets to true to make NAPI arm the TX coal timer.
+  */
+-static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue)
++static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
++                         bool *pending_packets)
+ {
+       struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
+       struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
+@@ -2698,7 +2702,7 @@ static int stmmac_tx_clean(struct stmmac
+       /* We still have pending packets, let's call for a new scheduling */
+       if (tx_q->dirty_tx != tx_q->cur_tx)
+-              stmmac_tx_timer_arm(priv, queue);
++              *pending_packets = true;
+       u64_stats_update_begin(&txq_stats->napi_syncp);
+       u64_stats_add(&txq_stats->napi.tx_packets, tx_packets);
+@@ -5603,6 +5607,7 @@ static int stmmac_napi_poll_tx(struct na
+               container_of(napi, struct stmmac_channel, tx_napi);
+       struct stmmac_priv *priv = ch->priv_data;
+       struct stmmac_txq_stats *txq_stats;
++      bool pending_packets = false;
+       u32 chan = ch->index;
+       int work_done;
+@@ -5611,7 +5616,7 @@ static int stmmac_napi_poll_tx(struct na
+       u64_stats_inc(&txq_stats->napi.poll);
+       u64_stats_update_end(&txq_stats->napi_syncp);
+-      work_done = stmmac_tx_clean(priv, budget, chan);
++      work_done = stmmac_tx_clean(priv, budget, chan, &pending_packets);
+       work_done = min(work_done, budget);
+       if (work_done < budget && napi_complete_done(napi, work_done)) {
+@@ -5622,6 +5627,10 @@ static int stmmac_napi_poll_tx(struct na
+               spin_unlock_irqrestore(&ch->lock, flags);
+       }
++      /* TX still have packet to handle, check if we need to arm tx timer */
++      if (pending_packets)
++              stmmac_tx_timer_arm(priv, chan);
++
+       return work_done;
+ }
+@@ -5630,6 +5639,7 @@ static int stmmac_napi_poll_rxtx(struct
+       struct stmmac_channel *ch =
+               container_of(napi, struct stmmac_channel, rxtx_napi);
+       struct stmmac_priv *priv = ch->priv_data;
++      bool tx_pending_packets = false;
+       int rx_done, tx_done, rxtx_done;
+       struct stmmac_rxq_stats *rxq_stats;
+       struct stmmac_txq_stats *txq_stats;
+@@ -5645,7 +5655,7 @@ static int stmmac_napi_poll_rxtx(struct
+       u64_stats_inc(&txq_stats->napi.poll);
+       u64_stats_update_end(&txq_stats->napi_syncp);
+-      tx_done = stmmac_tx_clean(priv, budget, chan);
++      tx_done = stmmac_tx_clean(priv, budget, chan, &tx_pending_packets);
+       tx_done = min(tx_done, budget);
+       rx_done = stmmac_rx_zc(priv, budget, chan);
+@@ -5670,6 +5680,10 @@ static int stmmac_napi_poll_rxtx(struct
+               spin_unlock_irqrestore(&ch->lock, flags);
+       }
++      /* TX still have packet to handle, check if we need to arm tx timer */
++      if (tx_pending_packets)
++              stmmac_tx_timer_arm(priv, chan);
++
+       return min(rxtx_done, budget - 1);
+ }
diff --git a/target/linux/generic/backport-6.6/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch b/target/linux/generic/backport-6.6/771-v6.7-03-net-stmmac-increase-TX-coalesce-timer-to-5ms.patch
new file mode 100644 (file)
index 0000000..f56c48b
--- /dev/null
@@ -0,0 +1,38 @@
+From 039550960a2235cfe2dfaa773df9f98f8da31a0c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 18 Oct 2023 14:35:50 +0200
+Subject: [PATCH 3/3] net: stmmac: increase TX coalesce timer to 5ms
+
+Commit 8fce33317023 ("net: stmmac: Rework coalesce timer and fix
+multi-queue races") decreased the TX coalesce timer from 40ms to 1ms.
+
+This caused some performance regression on some target (regression was
+reported at least on ipq806x) in the order of 600mbps dropping from
+gigabit handling to only 200mbps.
+
+The problem was identified in the TX timer getting armed too much time.
+While this was fixed and improved in another commit, performance can be
+improved even further by increasing the timer delay a bit moving from
+1ms to 5ms.
+
+The value is a good balance between battery saving by prevending too
+much interrupt to be generated and permitting good performance for
+internet oriented devices.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/stmicro/stmmac/common.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/stmicro/stmmac/common.h
++++ b/drivers/net/ethernet/stmicro/stmmac/common.h
+@@ -318,7 +318,7 @@ struct stmmac_safety_stats {
+ #define MIN_DMA_RIWT          0x10
+ #define DEF_DMA_RIWT          0xa0
+ /* Tx coalesce parameters */
+-#define STMMAC_COAL_TX_TIMER  1000
++#define STMMAC_COAL_TX_TIMER  5000
+ #define STMMAC_MAX_COAL_TX_TICK       100000
+ #define STMMAC_TX_MAX_FRAMES  256
+ #define STMMAC_TX_FRAMES      25
diff --git a/target/linux/generic/backport-6.6/780-01-v6.8-r8169-improve-RTL8411b-phy-down-fixup.patch b/target/linux/generic/backport-6.6/780-01-v6.8-r8169-improve-RTL8411b-phy-down-fixup.patch
new file mode 100644 (file)
index 0000000..b44627e
--- /dev/null
@@ -0,0 +1,173 @@
+From 055dd7511f675d26fa283b35bb3dadfc7f77ed97 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 13 Nov 2023 20:13:26 +0100
+Subject: [PATCH] r8169: improve RTL8411b phy-down fixup
+
+Mirsad proposed a patch to reduce the number of spinlock lock/unlock
+operations and the function code size. This can be further improved
+because the function sets a consecutive register block.
+
+Suggested-by: Mirsad Todorovac <mirsad.todorovac@alu.unizg.hr>
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Reviewed-by: Mirsad Todorovac <mirsad.todorovac@alu.unizg.hr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 139 +++++-----------------
+ 1 file changed, 28 insertions(+), 111 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -3156,6 +3156,33 @@ static void rtl_hw_start_8168g_2(struct
+       rtl_ephy_init(tp, e_info_8168g_2);
+ }
++static void rtl8411b_fix_phy_down(struct rtl8169_private *tp)
++{
++      static const u16 fix_data[] = {
++/* 0xf800 */ 0xe008, 0xe00a, 0xe00c, 0xe00e, 0xe027, 0xe04f, 0xe05e, 0xe065,
++/* 0xf810 */ 0xc602, 0xbe00, 0x0000, 0xc502, 0xbd00, 0x074c, 0xc302, 0xbb00,
++/* 0xf820 */ 0x080a, 0x6420, 0x48c2, 0x8c20, 0xc516, 0x64a4, 0x49c0, 0xf009,
++/* 0xf830 */ 0x74a2, 0x8ca5, 0x74a0, 0xc50e, 0x9ca2, 0x1c11, 0x9ca0, 0xe006,
++/* 0xf840 */ 0x74f8, 0x48c4, 0x8cf8, 0xc404, 0xbc00, 0xc403, 0xbc00, 0x0bf2,
++/* 0xf850 */ 0x0c0a, 0xe434, 0xd3c0, 0x49d9, 0xf01f, 0xc526, 0x64a5, 0x1400,
++/* 0xf860 */ 0xf007, 0x0c01, 0x8ca5, 0x1c15, 0xc51b, 0x9ca0, 0xe013, 0xc519,
++/* 0xf870 */ 0x74a0, 0x48c4, 0x8ca0, 0xc516, 0x74a4, 0x48c8, 0x48ca, 0x9ca4,
++/* 0xf880 */ 0xc512, 0x1b00, 0x9ba0, 0x1b1c, 0x483f, 0x9ba2, 0x1b04, 0xc508,
++/* 0xf890 */ 0x9ba0, 0xc505, 0xbd00, 0xc502, 0xbd00, 0x0300, 0x051e, 0xe434,
++/* 0xf8a0 */ 0xe018, 0xe092, 0xde20, 0xd3c0, 0xc50f, 0x76a4, 0x49e3, 0xf007,
++/* 0xf8b0 */ 0x49c0, 0xf103, 0xc607, 0xbe00, 0xc606, 0xbe00, 0xc602, 0xbe00,
++/* 0xf8c0 */ 0x0c4c, 0x0c28, 0x0c2c, 0xdc00, 0xc707, 0x1d00, 0x8de2, 0x48c1,
++/* 0xf8d0 */ 0xc502, 0xbd00, 0x00aa, 0xe0c0, 0xc502, 0xbd00, 0x0132
++      };
++      unsigned long flags;
++      int i;
++
++      raw_spin_lock_irqsave(&tp->mac_ocp_lock, flags);
++      for (i = 0; i < ARRAY_SIZE(fix_data); i++)
++              __r8168_mac_ocp_write(tp, 0xf800 + 2 * i, fix_data[i]);
++      raw_spin_unlock_irqrestore(&tp->mac_ocp_lock, flags);
++}
++
+ static void rtl_hw_start_8411_2(struct rtl8169_private *tp)
+ {
+       static const struct ephy_info e_info_8411_2[] = {
+@@ -3189,117 +3216,7 @@ static void rtl_hw_start_8411_2(struct r
+       mdelay(3);
+       r8168_mac_ocp_write(tp, 0xFC26, 0x0000);
+-      r8168_mac_ocp_write(tp, 0xF800, 0xE008);
+-      r8168_mac_ocp_write(tp, 0xF802, 0xE00A);
+-      r8168_mac_ocp_write(tp, 0xF804, 0xE00C);
+-      r8168_mac_ocp_write(tp, 0xF806, 0xE00E);
+-      r8168_mac_ocp_write(tp, 0xF808, 0xE027);
+-      r8168_mac_ocp_write(tp, 0xF80A, 0xE04F);
+-      r8168_mac_ocp_write(tp, 0xF80C, 0xE05E);
+-      r8168_mac_ocp_write(tp, 0xF80E, 0xE065);
+-      r8168_mac_ocp_write(tp, 0xF810, 0xC602);
+-      r8168_mac_ocp_write(tp, 0xF812, 0xBE00);
+-      r8168_mac_ocp_write(tp, 0xF814, 0x0000);
+-      r8168_mac_ocp_write(tp, 0xF816, 0xC502);
+-      r8168_mac_ocp_write(tp, 0xF818, 0xBD00);
+-      r8168_mac_ocp_write(tp, 0xF81A, 0x074C);
+-      r8168_mac_ocp_write(tp, 0xF81C, 0xC302);
+-      r8168_mac_ocp_write(tp, 0xF81E, 0xBB00);
+-      r8168_mac_ocp_write(tp, 0xF820, 0x080A);
+-      r8168_mac_ocp_write(tp, 0xF822, 0x6420);
+-      r8168_mac_ocp_write(tp, 0xF824, 0x48C2);
+-      r8168_mac_ocp_write(tp, 0xF826, 0x8C20);
+-      r8168_mac_ocp_write(tp, 0xF828, 0xC516);
+-      r8168_mac_ocp_write(tp, 0xF82A, 0x64A4);
+-      r8168_mac_ocp_write(tp, 0xF82C, 0x49C0);
+-      r8168_mac_ocp_write(tp, 0xF82E, 0xF009);
+-      r8168_mac_ocp_write(tp, 0xF830, 0x74A2);
+-      r8168_mac_ocp_write(tp, 0xF832, 0x8CA5);
+-      r8168_mac_ocp_write(tp, 0xF834, 0x74A0);
+-      r8168_mac_ocp_write(tp, 0xF836, 0xC50E);
+-      r8168_mac_ocp_write(tp, 0xF838, 0x9CA2);
+-      r8168_mac_ocp_write(tp, 0xF83A, 0x1C11);
+-      r8168_mac_ocp_write(tp, 0xF83C, 0x9CA0);
+-      r8168_mac_ocp_write(tp, 0xF83E, 0xE006);
+-      r8168_mac_ocp_write(tp, 0xF840, 0x74F8);
+-      r8168_mac_ocp_write(tp, 0xF842, 0x48C4);
+-      r8168_mac_ocp_write(tp, 0xF844, 0x8CF8);
+-      r8168_mac_ocp_write(tp, 0xF846, 0xC404);
+-      r8168_mac_ocp_write(tp, 0xF848, 0xBC00);
+-      r8168_mac_ocp_write(tp, 0xF84A, 0xC403);
+-      r8168_mac_ocp_write(tp, 0xF84C, 0xBC00);
+-      r8168_mac_ocp_write(tp, 0xF84E, 0x0BF2);
+-      r8168_mac_ocp_write(tp, 0xF850, 0x0C0A);
+-      r8168_mac_ocp_write(tp, 0xF852, 0xE434);
+-      r8168_mac_ocp_write(tp, 0xF854, 0xD3C0);
+-      r8168_mac_ocp_write(tp, 0xF856, 0x49D9);
+-      r8168_mac_ocp_write(tp, 0xF858, 0xF01F);
+-      r8168_mac_ocp_write(tp, 0xF85A, 0xC526);
+-      r8168_mac_ocp_write(tp, 0xF85C, 0x64A5);
+-      r8168_mac_ocp_write(tp, 0xF85E, 0x1400);
+-      r8168_mac_ocp_write(tp, 0xF860, 0xF007);
+-      r8168_mac_ocp_write(tp, 0xF862, 0x0C01);
+-      r8168_mac_ocp_write(tp, 0xF864, 0x8CA5);
+-      r8168_mac_ocp_write(tp, 0xF866, 0x1C15);
+-      r8168_mac_ocp_write(tp, 0xF868, 0xC51B);
+-      r8168_mac_ocp_write(tp, 0xF86A, 0x9CA0);
+-      r8168_mac_ocp_write(tp, 0xF86C, 0xE013);
+-      r8168_mac_ocp_write(tp, 0xF86E, 0xC519);
+-      r8168_mac_ocp_write(tp, 0xF870, 0x74A0);
+-      r8168_mac_ocp_write(tp, 0xF872, 0x48C4);
+-      r8168_mac_ocp_write(tp, 0xF874, 0x8CA0);
+-      r8168_mac_ocp_write(tp, 0xF876, 0xC516);
+-      r8168_mac_ocp_write(tp, 0xF878, 0x74A4);
+-      r8168_mac_ocp_write(tp, 0xF87A, 0x48C8);
+-      r8168_mac_ocp_write(tp, 0xF87C, 0x48CA);
+-      r8168_mac_ocp_write(tp, 0xF87E, 0x9CA4);
+-      r8168_mac_ocp_write(tp, 0xF880, 0xC512);
+-      r8168_mac_ocp_write(tp, 0xF882, 0x1B00);
+-      r8168_mac_ocp_write(tp, 0xF884, 0x9BA0);
+-      r8168_mac_ocp_write(tp, 0xF886, 0x1B1C);
+-      r8168_mac_ocp_write(tp, 0xF888, 0x483F);
+-      r8168_mac_ocp_write(tp, 0xF88A, 0x9BA2);
+-      r8168_mac_ocp_write(tp, 0xF88C, 0x1B04);
+-      r8168_mac_ocp_write(tp, 0xF88E, 0xC508);
+-      r8168_mac_ocp_write(tp, 0xF890, 0x9BA0);
+-      r8168_mac_ocp_write(tp, 0xF892, 0xC505);
+-      r8168_mac_ocp_write(tp, 0xF894, 0xBD00);
+-      r8168_mac_ocp_write(tp, 0xF896, 0xC502);
+-      r8168_mac_ocp_write(tp, 0xF898, 0xBD00);
+-      r8168_mac_ocp_write(tp, 0xF89A, 0x0300);
+-      r8168_mac_ocp_write(tp, 0xF89C, 0x051E);
+-      r8168_mac_ocp_write(tp, 0xF89E, 0xE434);
+-      r8168_mac_ocp_write(tp, 0xF8A0, 0xE018);
+-      r8168_mac_ocp_write(tp, 0xF8A2, 0xE092);
+-      r8168_mac_ocp_write(tp, 0xF8A4, 0xDE20);
+-      r8168_mac_ocp_write(tp, 0xF8A6, 0xD3C0);
+-      r8168_mac_ocp_write(tp, 0xF8A8, 0xC50F);
+-      r8168_mac_ocp_write(tp, 0xF8AA, 0x76A4);
+-      r8168_mac_ocp_write(tp, 0xF8AC, 0x49E3);
+-      r8168_mac_ocp_write(tp, 0xF8AE, 0xF007);
+-      r8168_mac_ocp_write(tp, 0xF8B0, 0x49C0);
+-      r8168_mac_ocp_write(tp, 0xF8B2, 0xF103);
+-      r8168_mac_ocp_write(tp, 0xF8B4, 0xC607);
+-      r8168_mac_ocp_write(tp, 0xF8B6, 0xBE00);
+-      r8168_mac_ocp_write(tp, 0xF8B8, 0xC606);
+-      r8168_mac_ocp_write(tp, 0xF8BA, 0xBE00);
+-      r8168_mac_ocp_write(tp, 0xF8BC, 0xC602);
+-      r8168_mac_ocp_write(tp, 0xF8BE, 0xBE00);
+-      r8168_mac_ocp_write(tp, 0xF8C0, 0x0C4C);
+-      r8168_mac_ocp_write(tp, 0xF8C2, 0x0C28);
+-      r8168_mac_ocp_write(tp, 0xF8C4, 0x0C2C);
+-      r8168_mac_ocp_write(tp, 0xF8C6, 0xDC00);
+-      r8168_mac_ocp_write(tp, 0xF8C8, 0xC707);
+-      r8168_mac_ocp_write(tp, 0xF8CA, 0x1D00);
+-      r8168_mac_ocp_write(tp, 0xF8CC, 0x8DE2);
+-      r8168_mac_ocp_write(tp, 0xF8CE, 0x48C1);
+-      r8168_mac_ocp_write(tp, 0xF8D0, 0xC502);
+-      r8168_mac_ocp_write(tp, 0xF8D2, 0xBD00);
+-      r8168_mac_ocp_write(tp, 0xF8D4, 0x00AA);
+-      r8168_mac_ocp_write(tp, 0xF8D6, 0xE0C0);
+-      r8168_mac_ocp_write(tp, 0xF8D8, 0xC502);
+-      r8168_mac_ocp_write(tp, 0xF8DA, 0xBD00);
+-      r8168_mac_ocp_write(tp, 0xF8DC, 0x0132);
++      rtl8411b_fix_phy_down(tp);
+       r8168_mac_ocp_write(tp, 0xFC26, 0x8000);
diff --git a/target/linux/generic/backport-6.6/780-02-v6.8-r8169-remove-not-needed-check-in-rtl_fw_write_firmwa.patch b/target/linux/generic/backport-6.6/780-02-v6.8-r8169-remove-not-needed-check-in-rtl_fw_write_firmwa.patch
new file mode 100644 (file)
index 0000000..de10d30
--- /dev/null
@@ -0,0 +1,27 @@
+From 3a767b482cacd9bfeac786837fcac419af315995 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Thu, 23 Nov 2023 10:53:26 +0100
+Subject: [PATCH] r8169: remove not needed check in rtl_fw_write_firmware
+
+This check can never be true for a firmware file with a correct format.
+Existing checks in rtl_fw_data_ok() are sufficient, no problems with
+invalid firmware files are known.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/realtek/r8169_firmware.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_firmware.c
++++ b/drivers/net/ethernet/realtek/r8169_firmware.c
+@@ -151,9 +151,6 @@ void rtl_fw_write_firmware(struct rtl816
+               u32 regno = (action & 0x0fff0000) >> 16;
+               enum rtl_fw_opcode opcode = action >> 28;
+-              if (!action)
+-                      break;
+-
+               switch (opcode) {
+               case PHY_READ:
+                       predata = fw_read(tp, regno);
diff --git a/target/linux/generic/backport-6.6/780-03-v6.8-r8169-remove-multicast-filter-limit.patch b/target/linux/generic/backport-6.6/780-03-v6.8-r8169-remove-multicast-filter-limit.patch
new file mode 100644 (file)
index 0000000..ad36db1
--- /dev/null
@@ -0,0 +1,47 @@
+From cd04b44bf055c4cd6bcee2ebfa6932fb20ef369d Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 27 Nov 2023 21:16:10 +0100
+Subject: [PATCH] r8169: remove multicast filter limit
+
+Once upon a time, when r8169 was new, the multicast filter limit code
+was copied from RTL8139 driver. There the filter limit is even
+user-configurable.
+The filtering is hash-based and we don't have perfect filtering.
+Actually the mc filtering on RTL8125 still seems to be the same
+as used on 8390/NE2000. So it's not clear to me which benefit it
+should bring when switching to all-multi mode once a certain number
+of filter bits is set. More the opposite: Filtering out at least
+some unwanted mc traffic is better than no filtering.
+Also the available chip documentation doesn't mention any restriction.
+Therefore remove the filter limit.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Link: https://lore.kernel.org/r/57076c05-3730-40d1-ab9a-5334b263e41a@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -56,10 +56,6 @@
+ #define FIRMWARE_8125A_3      "rtl_nic/rtl8125a-3.fw"
+ #define FIRMWARE_8125B_2      "rtl_nic/rtl8125b-2.fw"
+-/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+-   The RTL chips use a 64 element hash table based on the Ethernet CRC. */
+-#define       MC_FILTER_LIMIT 32
+-
+ #define TX_DMA_BURST  7       /* Maximum PCI burst, '7' is unlimited */
+ #define InterFrameGap 0x03    /* 3 means InterFrameGap = the shortest one */
+@@ -2653,8 +2649,7 @@ static void rtl_set_rx_mode(struct net_d
+               rx_mode |= AcceptAllPhys;
+       } else if (!(dev->flags & IFF_MULTICAST)) {
+               rx_mode &= ~AcceptMulticast;
+-      } else if (netdev_mc_count(dev) > MC_FILTER_LIMIT ||
+-                 dev->flags & IFF_ALLMULTI ||
++      } else if (dev->flags & IFF_ALLMULTI ||
+                  tp->mac_version == RTL_GIGA_MAC_VER_35) {
+               /* accept all multicasts */
+       } else if (netdev_mc_empty(dev)) {
diff --git a/target/linux/generic/backport-6.6/780-04-v6.8-r8169-improve-handling-task-scheduling.patch b/target/linux/generic/backport-6.6/780-04-v6.8-r8169-improve-handling-task-scheduling.patch
new file mode 100644 (file)
index 0000000..757c800
--- /dev/null
@@ -0,0 +1,41 @@
+From 127532cd0f060ebc3c4cbca81b6438728ad5896e Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 27 Nov 2023 18:20:11 +0100
+Subject: [PATCH] r8169: improve handling task scheduling
+
+If we know that the task is going to be a no-op, don't even schedule it.
+And remove the check for netif_running() in the worker function, the
+check for flag RTL_FLAG_TASK_ENABLED is sufficient. Note that we can't
+remove the check for flag RTL_FLAG_TASK_ENABLED in the worker function
+because we have no guarantee when it will be executed.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
+Link: https://lore.kernel.org/r/c65873a3-7394-4107-99a7-83f20030779c@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -2279,6 +2279,9 @@ u16 rtl8168h_2_get_adc_bias_ioffset(stru
+ static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag)
+ {
++      if (!test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
++              return;
++
+       set_bit(flag, tp->wk.flags);
+       schedule_work(&tp->wk.work);
+ }
+@@ -4520,8 +4523,7 @@ static void rtl_task(struct work_struct
+       rtnl_lock();
+-      if (!netif_running(tp->dev) ||
+-          !test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
++      if (!test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
+               goto out_unlock;
+       if (test_and_clear_bit(RTL_FLAG_TASK_TX_TIMEOUT, tp->wk.flags)) {
diff --git a/target/linux/generic/backport-6.6/780-05-v6.8-r8169-add-support-for-LED-s-on-RTL8168-RTL8101.patch b/target/linux/generic/backport-6.6/780-05-v6.8-r8169-add-support-for-LED-s-on-RTL8168-RTL8101.patch
new file mode 100644 (file)
index 0000000..0504a00
--- /dev/null
@@ -0,0 +1,319 @@
+From 18764b883e157e28126b54e7d4ba9dd487d5bf54 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Sat, 16 Dec 2023 20:58:10 +0100
+Subject: [PATCH] r8169: add support for LED's on RTL8168/RTL8101
+
+This adds support for the LED's on most chip versions. Excluded are
+the old non-PCIe versions and RTL8125. RTL8125 has a different LED
+register layout, support for it will follow later.
+
+LED's can be controlled from userspace using the netdev LED trigger.
+
+Tested on RTL8168h.
+
+Note: The driver can't know which LED's are actually physically
+wired. Therefore not every LED device may represent a physically
+available LED.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/realtek/Makefile     |   3 +
+ drivers/net/ethernet/realtek/r8169.h      |   7 +
+ drivers/net/ethernet/realtek/r8169_leds.c | 157 ++++++++++++++++++++++
+ drivers/net/ethernet/realtek/r8169_main.c |  65 +++++++++
+ 4 files changed, 232 insertions(+)
+ create mode 100644 drivers/net/ethernet/realtek/r8169_leds.c
+
+--- a/drivers/net/ethernet/realtek/Makefile
++++ b/drivers/net/ethernet/realtek/Makefile
+@@ -7,4 +7,7 @@ obj-$(CONFIG_8139CP) += 8139cp.o
+ obj-$(CONFIG_8139TOO) += 8139too.o
+ obj-$(CONFIG_ATP) += atp.o
+ r8169-objs += r8169_main.o r8169_firmware.o r8169_phy_config.o
++ifdef CONFIG_LEDS_TRIGGER_NETDEV
++r8169-objs += r8169_leds.o
++endif
+ obj-$(CONFIG_R8169) += r8169.o
+--- a/drivers/net/ethernet/realtek/r8169.h
++++ b/drivers/net/ethernet/realtek/r8169.h
+@@ -8,6 +8,7 @@
+  * See MAINTAINERS file for support contact information.
+  */
++#include <linux/netdevice.h>
+ #include <linux/types.h>
+ #include <linux/phy.h>
+@@ -77,3 +78,9 @@ u16 rtl8168h_2_get_adc_bias_ioffset(stru
+ u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr);
+ void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev,
+                        enum mac_version ver);
++
++void r8169_get_led_name(struct rtl8169_private *tp, int idx,
++                      char *buf, int buf_len);
++int rtl8168_get_led_mode(struct rtl8169_private *tp);
++int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
++void rtl8168_init_leds(struct net_device *ndev);
+--- /dev/null
++++ b/drivers/net/ethernet/realtek/r8169_leds.c
+@@ -0,0 +1,157 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* r8169_leds.c: Realtek 8169/8168/8101/8125 ethernet driver.
++ *
++ * Copyright (c) 2023 Heiner Kallweit <hkallweit1@gmail.com>
++ *
++ * See MAINTAINERS file for support contact information.
++ */
++
++#include <linux/leds.h>
++#include <linux/netdevice.h>
++#include <uapi/linux/uleds.h>
++
++#include "r8169.h"
++
++#define RTL8168_LED_CTRL_OPTION2      BIT(15)
++#define RTL8168_LED_CTRL_ACT          BIT(3)
++#define RTL8168_LED_CTRL_LINK_1000    BIT(2)
++#define RTL8168_LED_CTRL_LINK_100     BIT(1)
++#define RTL8168_LED_CTRL_LINK_10      BIT(0)
++
++#define RTL8168_NUM_LEDS              3
++
++#define RTL8168_SUPPORTED_MODES \
++      (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK_100) | \
++       BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_RX) | \
++       BIT(TRIGGER_NETDEV_TX))
++
++struct r8169_led_classdev {
++      struct led_classdev led;
++      struct net_device *ndev;
++      int index;
++};
++
++#define lcdev_to_r8169_ldev(lcdev) container_of(lcdev, struct r8169_led_classdev, led)
++
++static int rtl8168_led_hw_control_is_supported(struct led_classdev *led_cdev,
++                                             unsigned long flags)
++{
++      struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
++      struct rtl8169_private *tp = netdev_priv(ldev->ndev);
++      int shift = ldev->index * 4;
++      bool rx, tx;
++
++      if (flags & ~RTL8168_SUPPORTED_MODES)
++              goto nosupp;
++
++      rx = flags & BIT(TRIGGER_NETDEV_RX);
++      tx = flags & BIT(TRIGGER_NETDEV_TX);
++      if (rx != tx)
++              goto nosupp;
++
++      return 0;
++
++nosupp:
++      /* Switch LED off to indicate that mode isn't supported */
++      rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0);
++      return -EOPNOTSUPP;
++}
++
++static int rtl8168_led_hw_control_set(struct led_classdev *led_cdev,
++                                    unsigned long flags)
++{
++      struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
++      struct rtl8169_private *tp = netdev_priv(ldev->ndev);
++      int shift = ldev->index * 4;
++      u16 mode = 0;
++
++      if (flags & BIT(TRIGGER_NETDEV_LINK_10))
++              mode |= RTL8168_LED_CTRL_LINK_10;
++      if (flags & BIT(TRIGGER_NETDEV_LINK_100))
++              mode |= RTL8168_LED_CTRL_LINK_100;
++      if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
++              mode |= RTL8168_LED_CTRL_LINK_1000;
++      if (flags & BIT(TRIGGER_NETDEV_TX))
++              mode |= RTL8168_LED_CTRL_ACT;
++
++      return rtl8168_led_mod_ctrl(tp, 0x000f << shift, mode << shift);
++}
++
++static int rtl8168_led_hw_control_get(struct led_classdev *led_cdev,
++                                    unsigned long *flags)
++{
++      struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
++      struct rtl8169_private *tp = netdev_priv(ldev->ndev);
++      int shift = ldev->index * 4;
++      int mode;
++
++      mode = rtl8168_get_led_mode(tp);
++      if (mode < 0)
++              return mode;
++
++      if (mode & RTL8168_LED_CTRL_OPTION2) {
++              rtl8168_led_mod_ctrl(tp, RTL8168_LED_CTRL_OPTION2, 0);
++              netdev_notice(ldev->ndev, "Deactivating unsupported Option2 LED mode\n");
++      }
++
++      mode = (mode >> shift) & 0x000f;
++
++      if (mode & RTL8168_LED_CTRL_ACT)
++              *flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
++
++      if (mode & RTL8168_LED_CTRL_LINK_10)
++              *flags |= BIT(TRIGGER_NETDEV_LINK_10);
++      if (mode & RTL8168_LED_CTRL_LINK_100)
++              *flags |= BIT(TRIGGER_NETDEV_LINK_100);
++      if (mode & RTL8168_LED_CTRL_LINK_1000)
++              *flags |= BIT(TRIGGER_NETDEV_LINK_1000);
++
++      return 0;
++}
++
++static struct device *
++      r8169_led_hw_control_get_device(struct led_classdev *led_cdev)
++{
++      struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
++
++      return &ldev->ndev->dev;
++}
++
++static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev,
++                             struct net_device *ndev, int index)
++{
++      struct rtl8169_private *tp = netdev_priv(ndev);
++      struct led_classdev *led_cdev = &ldev->led;
++      char led_name[LED_MAX_NAME_SIZE];
++
++      ldev->ndev = ndev;
++      ldev->index = index;
++
++      r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);
++      led_cdev->name = led_name;
++      led_cdev->default_trigger = "netdev";
++      led_cdev->hw_control_trigger = "netdev";
++      led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
++      led_cdev->hw_control_is_supported = rtl8168_led_hw_control_is_supported;
++      led_cdev->hw_control_set = rtl8168_led_hw_control_set;
++      led_cdev->hw_control_get = rtl8168_led_hw_control_get;
++      led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
++
++      /* ignore errors */
++      devm_led_classdev_register(&ndev->dev, led_cdev);
++}
++
++void rtl8168_init_leds(struct net_device *ndev)
++{
++      /* bind resource mgmt to netdev */
++      struct device *dev = &ndev->dev;
++      struct r8169_led_classdev *leds;
++      int i;
++
++      leds = devm_kcalloc(dev, RTL8168_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
++      if (!leds)
++              return;
++
++      for (i = 0; i < RTL8168_NUM_LEDS; i++)
++              rtl8168_setup_ldev(leds + i, ndev, i);
++}
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -285,6 +285,7 @@ enum rtl8168_8101_registers {
+ };
+ enum rtl8168_registers {
++      LED_CTRL                = 0x18,
+       LED_FREQ                = 0x1a,
+       EEE_LED                 = 0x1b,
+       ERIDR                   = 0x70,
+@@ -643,6 +644,7 @@ struct rtl8169_private {
+       raw_spinlock_t config25_lock;
+       raw_spinlock_t mac_ocp_lock;
++      struct mutex led_lock;  /* serialize LED ctrl RMW access */
+       raw_spinlock_t cfg9346_usage_lock;
+       int cfg9346_usage_count;
+@@ -815,6 +817,62 @@ static const struct rtl_cond name = {
+                                                       \
+ static bool name ## _check(struct rtl8169_private *tp)
++int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val)
++{
++      struct device *dev = tp_to_dev(tp);
++      int ret;
++
++      ret = pm_runtime_resume_and_get(dev);
++      if (ret < 0)
++              return ret;
++
++      mutex_lock(&tp->led_lock);
++      RTL_W16(tp, LED_CTRL, (RTL_R16(tp, LED_CTRL) & ~mask) | val);
++      mutex_unlock(&tp->led_lock);
++
++      pm_runtime_put_sync(dev);
++
++      return 0;
++}
++
++int rtl8168_get_led_mode(struct rtl8169_private *tp)
++{
++      struct device *dev = tp_to_dev(tp);
++      int ret;
++
++      ret = pm_runtime_resume_and_get(dev);
++      if (ret < 0)
++              return ret;
++
++      ret = RTL_R16(tp, LED_CTRL);
++
++      pm_runtime_put_sync(dev);
++
++      return ret;
++}
++
++void r8169_get_led_name(struct rtl8169_private *tp, int idx,
++                      char *buf, int buf_len)
++{
++      struct pci_dev *pdev = tp->pci_dev;
++      char pdom[8], pfun[8];
++      int domain;
++
++      domain = pci_domain_nr(pdev->bus);
++      if (domain)
++              snprintf(pdom, sizeof(pdom), "P%d", domain);
++      else
++              pdom[0] = '\0';
++
++      if (pdev->multifunction)
++              snprintf(pfun, sizeof(pfun), "f%d", PCI_FUNC(pdev->devfn));
++      else
++              pfun[0] = '\0';
++
++      snprintf(buf, buf_len, "en%sp%ds%d%s-%d::lan", pdom, pdev->bus->number,
++               PCI_SLOT(pdev->devfn), pfun, idx);
++}
++
+ static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type)
+ {
+       /* based on RTL8168FP_OOBMAC_BASE in vendor driver */
+@@ -5197,6 +5255,7 @@ static int rtl_init_one(struct pci_dev *
+       raw_spin_lock_init(&tp->cfg9346_usage_lock);
+       raw_spin_lock_init(&tp->config25_lock);
+       raw_spin_lock_init(&tp->mac_ocp_lock);
++      mutex_init(&tp->led_lock);
+       dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev,
+                                                  struct pcpu_sw_netstats);
+@@ -5353,6 +5412,12 @@ static int rtl_init_one(struct pci_dev *
+       if (rc)
+               return rc;
++#if IS_REACHABLE(CONFIG_LEDS_CLASS) && IS_ENABLED(CONFIG_LEDS_TRIGGER_NETDEV)
++      if (tp->mac_version > RTL_GIGA_MAC_VER_06 &&
++          tp->mac_version < RTL_GIGA_MAC_VER_61)
++              rtl8168_init_leds(dev);
++#endif
++
+       netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
+                   rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq);
diff --git a/target/linux/generic/backport-6.6/780-06-v6.8-r8169-fix-building-with-CONFIG_LEDS_CLASS-m.patch b/target/linux/generic/backport-6.6/780-06-v6.8-r8169-fix-building-with-CONFIG_LEDS_CLASS-m.patch
new file mode 100644 (file)
index 0000000..1adaf87
--- /dev/null
@@ -0,0 +1,75 @@
+From a2634a5ffcafc31c343c6153ae487eb184c433a6 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Wed, 3 Jan 2024 16:52:04 +0100
+Subject: [PATCH] r8169: fix building with CONFIG_LEDS_CLASS=m
+
+When r8169 is built-in but LED support is a loadable module, the new
+code to drive the LED causes a link failure:
+
+ld: drivers/net/ethernet/realtek/r8169_leds.o: in function `rtl8168_init_leds':
+r8169_leds.c:(.text+0x36c): undefined reference to `devm_led_classdev_register_ext'
+
+LED support is an optional feature, so fix this issue by adding a Kconfig
+symbol R8169_LEDS that is guaranteed to be false if r8169 is built-in
+and LED core support is a module. As a positive side effect of this change
+r8169_leds.o no longer is built under this configuration.
+
+Fixes: 18764b883e15 ("r8169: add support for LED's on RTL8168/RTL8101")
+Reported-by: kernel test robot <lkp@intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202312281159.9TPeXbNd-lkp@intel.com/
+Suggested-by: Arnd Bergmann <arnd@arndb.de>
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Tested-by: Simon Horman <horms@kernel.org> # build-tested
+Tested-by: Arnd Bergmann <arnd@arndb.de>
+Link: https://lore.kernel.org/r/d055aeb5-fe5c-4ccf-987f-5af93a17537b@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/Kconfig      | 7 +++++++
+ drivers/net/ethernet/realtek/Makefile     | 6 ++----
+ drivers/net/ethernet/realtek/r8169_main.c | 5 ++---
+ 3 files changed, 11 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/Kconfig
++++ b/drivers/net/ethernet/realtek/Kconfig
+@@ -113,4 +113,11 @@ config R8169
+         To compile this driver as a module, choose M here: the module
+         will be called r8169.  This is recommended.
++config R8169_LEDS
++      def_bool R8169 && LEDS_TRIGGER_NETDEV
++      depends on !(R8169=y && LEDS_CLASS=m)
++      help
++        Optional support for controlling the NIC LED's with the netdev
++        LED trigger.
++
+ endif # NET_VENDOR_REALTEK
+--- a/drivers/net/ethernet/realtek/Makefile
++++ b/drivers/net/ethernet/realtek/Makefile
+@@ -6,8 +6,6 @@
+ obj-$(CONFIG_8139CP) += 8139cp.o
+ obj-$(CONFIG_8139TOO) += 8139too.o
+ obj-$(CONFIG_ATP) += atp.o
+-r8169-objs += r8169_main.o r8169_firmware.o r8169_phy_config.o
+-ifdef CONFIG_LEDS_TRIGGER_NETDEV
+-r8169-objs += r8169_leds.o
+-endif
++r8169-y += r8169_main.o r8169_firmware.o r8169_phy_config.o
++r8169-$(CONFIG_R8169_LEDS) += r8169_leds.o
+ obj-$(CONFIG_R8169) += r8169.o
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -5412,11 +5412,10 @@ static int rtl_init_one(struct pci_dev *
+       if (rc)
+               return rc;
+-#if IS_REACHABLE(CONFIG_LEDS_CLASS) && IS_ENABLED(CONFIG_LEDS_TRIGGER_NETDEV)
+-      if (tp->mac_version > RTL_GIGA_MAC_VER_06 &&
++      if (IS_ENABLED(CONFIG_R8169_LEDS) &&
++          tp->mac_version > RTL_GIGA_MAC_VER_06 &&
+           tp->mac_version < RTL_GIGA_MAC_VER_61)
+               rtl8168_init_leds(dev);
+-#endif
+       netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
+                   rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq);
diff --git a/target/linux/generic/backport-6.6/780-07-v6.9-r8169-simplify-EEE-handling.patch b/target/linux/generic/backport-6.6/780-07-v6.9-r8169-simplify-EEE-handling.patch
new file mode 100644 (file)
index 0000000..c3f94b3
--- /dev/null
@@ -0,0 +1,96 @@
+From f5d59230ec26aa5e8b59e9f4a4d288703a737479 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Wed, 31 Jan 2024 21:31:01 +0100
+Subject: [PATCH] r8169: simplify EEE handling
+
+We don't have to store the EEE modes to be advertised in the driver,
+phylib does this for us and stores it in phydev->advertising_eee.
+phylib also takes care of properly handling the EEE advertisement.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Link: https://lore.kernel.org/r/27c336a8-ea47-483d-815b-02c45ae41da2@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 32 +++--------------------
+ 1 file changed, 4 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -656,7 +656,6 @@ struct rtl8169_private {
+       struct rtl8169_counters *counters;
+       struct rtl8169_tc_offsets tc_offset;
+       u32 saved_wolopts;
+-      int eee_adv;
+       const char *fw_name;
+       struct rtl_fw *rtl_fw;
+@@ -2037,17 +2036,11 @@ static int rtl8169_get_eee(struct net_de
+ static int rtl8169_set_eee(struct net_device *dev, struct ethtool_eee *data)
+ {
+       struct rtl8169_private *tp = netdev_priv(dev);
+-      int ret;
+       if (!rtl_supports_eee(tp))
+               return -EOPNOTSUPP;
+-      ret = phy_ethtool_set_eee(tp->phydev, data);
+-
+-      if (!ret)
+-              tp->eee_adv = phy_read_mmd(dev->phydev, MDIO_MMD_AN,
+-                                         MDIO_AN_EEE_ADV);
+-      return ret;
++      return phy_ethtool_set_eee(tp->phydev, data);
+ }
+ static void rtl8169_get_ringparam(struct net_device *dev,
+@@ -2112,21 +2105,6 @@ static const struct ethtool_ops rtl8169_
+       .set_pauseparam         = rtl8169_set_pauseparam,
+ };
+-static void rtl_enable_eee(struct rtl8169_private *tp)
+-{
+-      struct phy_device *phydev = tp->phydev;
+-      int adv;
+-
+-      /* respect EEE advertisement the user may have set */
+-      if (tp->eee_adv >= 0)
+-              adv = tp->eee_adv;
+-      else
+-              adv = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
+-
+-      if (adv >= 0)
+-              phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
+-}
+-
+ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii)
+ {
+       /*
+@@ -2363,9 +2341,6 @@ static void rtl8169_init_phy(struct rtl8
+       /* We may have called phy_speed_down before */
+       phy_speed_up(tp->phydev);
+-      if (rtl_supports_eee(tp))
+-              rtl_enable_eee(tp);
+-
+       genphy_soft_reset(tp->phydev);
+ }
+@@ -5114,7 +5089,9 @@ static int r8169_mdio_register(struct rt
+       }
+       tp->phydev->mac_managed_pm = true;
+-
++      if (rtl_supports_eee(tp))
++              linkmode_copy(tp->phydev->advertising_eee,
++                            tp->phydev->supported_eee);
+       phy_support_asym_pause(tp->phydev);
+       /* PHY will be woken up in rtl_open() */
+@@ -5249,7 +5226,6 @@ static int rtl_init_one(struct pci_dev *
+       tp->dev = dev;
+       tp->pci_dev = pdev;
+       tp->supports_gmii = ent->driver_data == RTL_CFG_NO_GBIT ? 0 : 1;
+-      tp->eee_adv = -1;
+       tp->ocp_base = OCP_STD_PHY_BASE;
+       raw_spin_lock_init(&tp->cfg9346_usage_lock);
diff --git a/target/linux/generic/backport-6.6/780-08-v6.9-r8169-add-support-for-RTL8126A.patch b/target/linux/generic/backport-6.6/780-08-v6.9-r8169-add-support-for-RTL8126A.patch
new file mode 100644 (file)
index 0000000..354aacf
--- /dev/null
@@ -0,0 +1,355 @@
+From 3907f1ffc0ecf466d5c04aadc44c4b9203f3ec9a Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Thu, 1 Feb 2024 22:38:01 +0100
+Subject: [PATCH] r8169: add support for RTL8126A
+
+This adds support for the RTL8126A found on Asus z790 Maximus Formula.
+It was successfully tested w/o the firmware at 1000Mbps. Firmware file
+has been provided by Realtek and submitted to linux-firmware.
+2.5G and 5G modes are untested.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/realtek/r8169.h          |   1 +
+ drivers/net/ethernet/realtek/r8169_main.c     | 105 ++++++++++++++----
+ .../net/ethernet/realtek/r8169_phy_config.c   |   7 ++
+ 3 files changed, 89 insertions(+), 24 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169.h
++++ b/drivers/net/ethernet/realtek/r8169.h
+@@ -68,6 +68,7 @@ enum mac_version {
+       /* support for RTL_GIGA_MAC_VER_60 has been removed */
+       RTL_GIGA_MAC_VER_61,
+       RTL_GIGA_MAC_VER_63,
++      RTL_GIGA_MAC_VER_65,
+       RTL_GIGA_MAC_NONE
+ };
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -55,6 +55,7 @@
+ #define FIRMWARE_8107E_2      "rtl_nic/rtl8107e-2.fw"
+ #define FIRMWARE_8125A_3      "rtl_nic/rtl8125a-3.fw"
+ #define FIRMWARE_8125B_2      "rtl_nic/rtl8125b-2.fw"
++#define FIRMWARE_8126A_2      "rtl_nic/rtl8126a-2.fw"
+ #define TX_DMA_BURST  7       /* Maximum PCI burst, '7' is unlimited */
+ #define InterFrameGap 0x03    /* 3 means InterFrameGap = the shortest one */
+@@ -136,6 +137,7 @@ static const struct {
+       [RTL_GIGA_MAC_VER_61] = {"RTL8125A",            FIRMWARE_8125A_3},
+       /* reserve 62 for CFG_METHOD_4 in the vendor driver */
+       [RTL_GIGA_MAC_VER_63] = {"RTL8125B",            FIRMWARE_8125B_2},
++      [RTL_GIGA_MAC_VER_65] = {"RTL8126A",            FIRMWARE_8126A_2},
+ };
+ static const struct pci_device_id rtl8169_pci_tbl[] = {
+@@ -158,6 +160,7 @@ static const struct pci_device_id rtl816
+       { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0024 },
+       { 0x0001, 0x8168, PCI_ANY_ID, 0x2410 },
+       { PCI_VDEVICE(REALTEK,  0x8125) },
++      { PCI_VDEVICE(REALTEK,  0x8126) },
+       { PCI_VDEVICE(REALTEK,  0x3000) },
+       {}
+ };
+@@ -327,8 +330,12 @@ enum rtl8168_registers {
+ };
+ enum rtl8125_registers {
++      INT_CFG0_8125           = 0x34,
++#define INT_CFG0_ENABLE_8125          BIT(0)
++#define INT_CFG0_CLKREQEN             BIT(3)
+       IntrMask_8125           = 0x38,
+       IntrStatus_8125         = 0x3c,
++      INT_CFG1_8125           = 0x7a,
+       TxPoll_8125             = 0x90,
+       MAC0_BKP                = 0x19e0,
+       EEE_TXIDLE_TIMER_8125   = 0x6048,
+@@ -1166,7 +1173,7 @@ static void rtl_writephy(struct rtl8169_
+       case RTL_GIGA_MAC_VER_31:
+               r8168dp_2_mdio_write(tp, location, val);
+               break;
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
+               r8168g_mdio_write(tp, location, val);
+               break;
+       default:
+@@ -1181,7 +1188,7 @@ static int rtl_readphy(struct rtl8169_pr
+       case RTL_GIGA_MAC_VER_28:
+       case RTL_GIGA_MAC_VER_31:
+               return r8168dp_2_mdio_read(tp, location);
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
+               return r8168g_mdio_read(tp, location);
+       default:
+               return r8169_mdio_read(tp, location);
+@@ -1390,7 +1397,7 @@ static void rtl_set_d3_pll_down(struct r
+       case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26:
+       case RTL_GIGA_MAC_VER_29 ... RTL_GIGA_MAC_VER_30:
+       case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37:
+-      case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65:
+               if (enable)
+                       RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN);
+               else
+@@ -1557,7 +1564,7 @@ static void __rtl8169_set_wol(struct rtl
+               break;
+       case RTL_GIGA_MAC_VER_34:
+       case RTL_GIGA_MAC_VER_37:
+-      case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65:
+               if (wolopts)
+                       rtl_mod_config2(tp, 0, PME_SIGNAL);
+               else
+@@ -2123,6 +2130,9 @@ static enum mac_version rtl8169_get_mac_
+               u16 val;
+               enum mac_version ver;
+       } mac_info[] = {
++              /* 8126A family. */
++              { 0x7cf, 0x649, RTL_GIGA_MAC_VER_65 },
++
+               /* 8125B family. */
+               { 0x7cf, 0x641, RTL_GIGA_MAC_VER_63 },
+@@ -2393,6 +2403,7 @@ static void rtl_init_rxcfg(struct rtl816
+               RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST);
+               break;
+       case RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_65:
+               RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST |
+                       RX_PAUSE_SLOT_ON);
+               break;
+@@ -2579,7 +2590,7 @@ static void rtl_wait_txrx_fifo_empty(str
+       case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61:
+               rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
+               break;
+-      case RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_65:
+               RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
+               rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
+               rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42);
+@@ -2822,7 +2833,7 @@ static void rtl_enable_exit_l1(struct rt
+       case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38:
+               rtl_eri_set_bits(tp, 0xd4, 0x0c00);
+               break;
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
+               r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80);
+               break;
+       default:
+@@ -2836,7 +2847,7 @@ static void rtl_disable_exit_l1(struct r
+       case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38:
+               rtl_eri_clear_bits(tp, 0xd4, 0x1f00);
+               break;
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
+               r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0);
+               break;
+       default:
+@@ -2846,6 +2857,8 @@ static void rtl_disable_exit_l1(struct r
+ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
+ {
++      u8 val8;
++
+       if (tp->mac_version < RTL_GIGA_MAC_VER_32)
+               return;
+@@ -2859,11 +2872,19 @@ static void rtl_hw_aspm_clkreq_enable(st
+                       return;
+               rtl_mod_config5(tp, 0, ASPM_en);
+-              rtl_mod_config2(tp, 0, ClkReqEn);
++              switch (tp->mac_version) {
++              case RTL_GIGA_MAC_VER_65:
++                      val8 = RTL_R8(tp, INT_CFG0_8125) | INT_CFG0_CLKREQEN;
++                      RTL_W8(tp, INT_CFG0_8125, val8);
++                      break;
++              default:
++                      rtl_mod_config2(tp, 0, ClkReqEn);
++                      break;
++              }
+               switch (tp->mac_version) {
+               case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
+-              case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
++              case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
+                       /* reset ephy tx/rx disable timer */
+                       r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0);
+                       /* chip can trigger L1.2 */
+@@ -2875,14 +2896,22 @@ static void rtl_hw_aspm_clkreq_enable(st
+       } else {
+               switch (tp->mac_version) {
+               case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
+-              case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
++              case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
+                       r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0);
+                       break;
+               default:
+                       break;
+               }
+-              rtl_mod_config2(tp, ClkReqEn, 0);
++              switch (tp->mac_version) {
++              case RTL_GIGA_MAC_VER_65:
++                      val8 = RTL_R8(tp, INT_CFG0_8125) & ~INT_CFG0_CLKREQEN;
++                      RTL_W8(tp, INT_CFG0_8125, val8);
++                      break;
++              default:
++                      rtl_mod_config2(tp, ClkReqEn, 0);
++                      break;
++              }
+               rtl_mod_config5(tp, ASPM_en, 0);
+       }
+ }
+@@ -3595,10 +3624,15 @@ static void rtl_hw_start_8125_common(str
+       /* disable new tx descriptor format */
+       r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000);
+-      if (tp->mac_version == RTL_GIGA_MAC_VER_63)
++      if (tp->mac_version == RTL_GIGA_MAC_VER_65)
++              RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02);
++
++      if (tp->mac_version == RTL_GIGA_MAC_VER_65)
++              r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400);
++      else if (tp->mac_version == RTL_GIGA_MAC_VER_63)
+               r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200);
+       else
+-              r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400);
++              r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0300);
+       if (tp->mac_version == RTL_GIGA_MAC_VER_63)
+               r8168_mac_ocp_modify(tp, 0xe63e, 0x0c30, 0x0000);
+@@ -3611,6 +3645,10 @@ static void rtl_hw_start_8125_common(str
+       r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0030);
+       r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000);
+       r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001);
++      if (tp->mac_version == RTL_GIGA_MAC_VER_65)
++              r8168_mac_ocp_modify(tp, 0xea1c, 0x0300, 0x0000);
++      else
++              r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000);
+       r8168_mac_ocp_modify(tp, 0xe0c0, 0x4f0f, 0x4403);
+       r8168_mac_ocp_modify(tp, 0xe052, 0x0080, 0x0068);
+       r8168_mac_ocp_modify(tp, 0xd430, 0x0fff, 0x047f);
+@@ -3625,10 +3663,10 @@ static void rtl_hw_start_8125_common(str
+       rtl_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10);
+-      if (tp->mac_version == RTL_GIGA_MAC_VER_63)
+-              rtl8125b_config_eee_mac(tp);
+-      else
++      if (tp->mac_version == RTL_GIGA_MAC_VER_61)
+               rtl8125a_config_eee_mac(tp);
++      else
++              rtl8125b_config_eee_mac(tp);
+       rtl_disable_rxdvgate(tp);
+ }
+@@ -3672,6 +3710,12 @@ static void rtl_hw_start_8125b(struct rt
+       rtl_hw_start_8125_common(tp);
+ }
++static void rtl_hw_start_8126a(struct rtl8169_private *tp)
++{
++      rtl_set_def_aspm_entry_latency(tp);
++      rtl_hw_start_8125_common(tp);
++}
++
+ static void rtl_hw_config(struct rtl8169_private *tp)
+ {
+       static const rtl_generic_fct hw_configs[] = {
+@@ -3714,6 +3758,7 @@ static void rtl_hw_config(struct rtl8169
+               [RTL_GIGA_MAC_VER_53] = rtl_hw_start_8117,
+               [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2,
+               [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b,
++              [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8126a,
+       };
+       if (hw_configs[tp->mac_version])
+@@ -3724,9 +3769,23 @@ static void rtl_hw_start_8125(struct rtl
+ {
+       int i;
++      RTL_W8(tp, INT_CFG0_8125, 0x00);
++
+       /* disable interrupt coalescing */
+-      for (i = 0xa00; i < 0xb00; i += 4)
+-              RTL_W32(tp, i, 0);
++      switch (tp->mac_version) {
++      case RTL_GIGA_MAC_VER_61:
++              for (i = 0xa00; i < 0xb00; i += 4)
++                      RTL_W32(tp, i, 0);
++              break;
++      case RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_65:
++              for (i = 0xa00; i < 0xa80; i += 4)
++                      RTL_W32(tp, i, 0);
++              RTL_W16(tp, INT_CFG1_8125, 0x0000);
++              break;
++      default:
++              break;
++      }
+       rtl_hw_config(tp);
+ }
+@@ -3804,8 +3863,7 @@ static int rtl8169_change_mtu(struct net
+       rtl_jumbo_config(tp);
+       switch (tp->mac_version) {
+-      case RTL_GIGA_MAC_VER_61:
+-      case RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
+               rtl8125_set_eee_txidle_timer(tp);
+               break;
+       default:
+@@ -3954,7 +4012,7 @@ static void rtl8169_cleanup(struct rtl81
+               RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
+               rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
+               break;
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
+               rtl_enable_rxdvgate(tp);
+               fsleep(2000);
+               break;
+@@ -4105,8 +4163,7 @@ static unsigned int rtl_quirk_packet_pad
+       switch (tp->mac_version) {
+       case RTL_GIGA_MAC_VER_34:
+-      case RTL_GIGA_MAC_VER_61:
+-      case RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
+               padto = max_t(unsigned int, padto, ETH_ZLEN);
+               break;
+       default:
+@@ -5141,7 +5198,7 @@ static void rtl_hw_initialize(struct rtl
+       case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48:
+               rtl_hw_init_8168g(tp);
+               break;
+-      case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
+               rtl_hw_init_8125(tp);
+               break;
+       default:
+--- a/drivers/net/ethernet/realtek/r8169_phy_config.c
++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c
+@@ -1104,6 +1104,12 @@ static void rtl8125b_hw_phy_config(struc
+       rtl8125b_config_eee_phy(phydev);
+ }
++static void rtl8126a_hw_phy_config(struct rtl8169_private *tp,
++                                 struct phy_device *phydev)
++{
++      r8169_apply_firmware(tp);
++}
++
+ void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev,
+                        enum mac_version ver)
+ {
+@@ -1154,6 +1160,7 @@ void r8169_hw_phy_config(struct rtl8169_
+               [RTL_GIGA_MAC_VER_53] = rtl8117_hw_phy_config,
+               [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config,
+               [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config,
++              [RTL_GIGA_MAC_VER_65] = rtl8126a_hw_phy_config,
+       };
+       if (phy_configs[ver])
diff --git a/target/linux/generic/backport-6.6/780-09-v6.9-r8169-improve-checking-for-valid-LED-modes.patch b/target/linux/generic/backport-6.6/780-09-v6.9-r8169-improve-checking-for-valid-LED-modes.patch
new file mode 100644 (file)
index 0000000..ae0c821
--- /dev/null
@@ -0,0 +1,81 @@
+From 4c49b6824a607af4760fac4f5c0b9954ab902cef Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Wed, 7 Feb 2024 08:16:40 +0100
+Subject: [PATCH] r8169: improve checking for valid LED modes
+
+After 3a2746320403 ("leds: trigger: netdev: Display only supported link
+speed attribute") the check for valid link modes can be simplified.
+In addition factor it out, so that it can be re-used by the upcoming
+LED support for RTL8125.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://lore.kernel.org/r/8876a9f4-7a2d-48c3-8eae-0d834f5c27c5@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_leds.c | 38 ++++++++++++-----------
+ 1 file changed, 20 insertions(+), 18 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_leds.c
++++ b/drivers/net/ethernet/realtek/r8169_leds.c
+@@ -20,11 +20,6 @@
+ #define RTL8168_NUM_LEDS              3
+-#define RTL8168_SUPPORTED_MODES \
+-      (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK_100) | \
+-       BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_RX) | \
+-       BIT(TRIGGER_NETDEV_TX))
+-
+ struct r8169_led_classdev {
+       struct led_classdev led;
+       struct net_device *ndev;
+@@ -33,28 +28,35 @@ struct r8169_led_classdev {
+ #define lcdev_to_r8169_ldev(lcdev) container_of(lcdev, struct r8169_led_classdev, led)
++static bool r8169_trigger_mode_is_valid(unsigned long flags)
++{
++      bool rx, tx;
++
++      if (flags & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
++              return false;
++      if (flags & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
++              return false;
++
++      rx = flags & BIT(TRIGGER_NETDEV_RX);
++      tx = flags & BIT(TRIGGER_NETDEV_TX);
++
++      return rx == tx;
++}
++
+ static int rtl8168_led_hw_control_is_supported(struct led_classdev *led_cdev,
+                                              unsigned long flags)
+ {
+       struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
+       struct rtl8169_private *tp = netdev_priv(ldev->ndev);
+       int shift = ldev->index * 4;
+-      bool rx, tx;
+-      if (flags & ~RTL8168_SUPPORTED_MODES)
+-              goto nosupp;
+-
+-      rx = flags & BIT(TRIGGER_NETDEV_RX);
+-      tx = flags & BIT(TRIGGER_NETDEV_TX);
+-      if (rx != tx)
+-              goto nosupp;
++      if (!r8169_trigger_mode_is_valid(flags)) {
++              /* Switch LED off to indicate that mode isn't supported */
++              rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0);
++              return -EOPNOTSUPP;
++      }
+       return 0;
+-
+-nosupp:
+-      /* Switch LED off to indicate that mode isn't supported */
+-      rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0);
+-      return -EOPNOTSUPP;
+ }
+ static int rtl8168_led_hw_control_set(struct led_classdev *led_cdev,
diff --git a/target/linux/generic/backport-6.6/780-10-v6.9-r8169-simplify-code-by-using-core-provided-pcpu-stat.patch b/target/linux/generic/backport-6.6/780-10-v6.9-r8169-simplify-code-by-using-core-provided-pcpu-stat.patch
new file mode 100644 (file)
index 0000000..c155b11
--- /dev/null
@@ -0,0 +1,39 @@
+From 400909df6e6543cb5cce3db9bbcd413d59125327 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Sat, 10 Feb 2024 17:58:29 +0100
+Subject: [PATCH] r8169: simplify code by using core-provided pcpu stats
+ allocation
+
+Use core-provided pcpu stats allocation instead of open-coding it in
+the driver.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Link: https://lore.kernel.org/r/03f5bb3b-d7f4-48be-ae8a-54862ec4566c@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -5290,11 +5290,6 @@ static int rtl_init_one(struct pci_dev *
+       raw_spin_lock_init(&tp->mac_ocp_lock);
+       mutex_init(&tp->led_lock);
+-      dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev,
+-                                                 struct pcpu_sw_netstats);
+-      if (!dev->tstats)
+-              return -ENOMEM;
+-
+       /* Get the *optional* external "ether_clk" used on some boards */
+       tp->clk = devm_clk_get_optional_enabled(&pdev->dev, "ether_clk");
+       if (IS_ERR(tp->clk))
+@@ -5409,6 +5404,8 @@ static int rtl_init_one(struct pci_dev *
+       dev->hw_features |= NETIF_F_RXALL;
+       dev->hw_features |= NETIF_F_RXFCS;
++      dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
++
+       netdev_sw_irq_coalesce_default_on(dev);
+       /* configure chip for default features */
diff --git a/target/linux/generic/backport-6.6/780-11-v6.9-r8169-add-LED-support-for-RTL8125-RTL8126.patch b/target/linux/generic/backport-6.6/780-11-v6.9-r8169-add-LED-support-for-RTL8125-RTL8126.patch
new file mode 100644 (file)
index 0000000..1c793f9
--- /dev/null
@@ -0,0 +1,244 @@
+From be51ed104ba9929c741afb718ef7198dbcecef94 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 12 Feb 2024 19:44:11 +0100
+Subject: [PATCH] r8169: add LED support for RTL8125/RTL8126
+
+This adds LED support for RTL8125/RTL8126.
+
+Note: Due to missing datasheets changing the 5Gbps link mode isn't
+supported for RTL8126.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/f982602c-9de3-4ca6-85a3-2c1d118dcb15@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169.h      |   3 +
+ drivers/net/ethernet/realtek/r8169_leds.c | 106 ++++++++++++++++++++++
+ drivers/net/ethernet/realtek/r8169_main.c |  61 ++++++++++++-
+ 3 files changed, 166 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169.h
++++ b/drivers/net/ethernet/realtek/r8169.h
+@@ -85,3 +85,6 @@ void r8169_get_led_name(struct rtl8169_p
+ int rtl8168_get_led_mode(struct rtl8169_private *tp);
+ int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
+ void rtl8168_init_leds(struct net_device *ndev);
++int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
++int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
++void rtl8125_init_leds(struct net_device *ndev);
+--- a/drivers/net/ethernet/realtek/r8169_leds.c
++++ b/drivers/net/ethernet/realtek/r8169_leds.c
+@@ -18,7 +18,14 @@
+ #define RTL8168_LED_CTRL_LINK_100     BIT(1)
+ #define RTL8168_LED_CTRL_LINK_10      BIT(0)
++#define RTL8125_LED_CTRL_ACT          BIT(9)
++#define RTL8125_LED_CTRL_LINK_2500    BIT(5)
++#define RTL8125_LED_CTRL_LINK_1000    BIT(3)
++#define RTL8125_LED_CTRL_LINK_100     BIT(1)
++#define RTL8125_LED_CTRL_LINK_10      BIT(0)
++
+ #define RTL8168_NUM_LEDS              3
++#define RTL8125_NUM_LEDS              4
+ struct r8169_led_classdev {
+       struct led_classdev led;
+@@ -157,3 +164,102 @@ void rtl8168_init_leds(struct net_device
+       for (i = 0; i < RTL8168_NUM_LEDS; i++)
+               rtl8168_setup_ldev(leds + i, ndev, i);
+ }
++
++static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,
++                                             unsigned long flags)
++{
++      struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
++      struct rtl8169_private *tp = netdev_priv(ldev->ndev);
++
++      if (!r8169_trigger_mode_is_valid(flags)) {
++              /* Switch LED off to indicate that mode isn't supported */
++              rtl8125_set_led_mode(tp, ldev->index, 0);
++              return -EOPNOTSUPP;
++      }
++
++      return 0;
++}
++
++static int rtl8125_led_hw_control_set(struct led_classdev *led_cdev,
++                                    unsigned long flags)
++{
++      struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
++      struct rtl8169_private *tp = netdev_priv(ldev->ndev);
++      u16 mode = 0;
++
++      if (flags & BIT(TRIGGER_NETDEV_LINK_10))
++              mode |= RTL8125_LED_CTRL_LINK_10;
++      if (flags & BIT(TRIGGER_NETDEV_LINK_100))
++              mode |= RTL8125_LED_CTRL_LINK_100;
++      if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
++              mode |= RTL8125_LED_CTRL_LINK_1000;
++      if (flags & BIT(TRIGGER_NETDEV_LINK_2500))
++              mode |= RTL8125_LED_CTRL_LINK_2500;
++      if (flags & (BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX)))
++              mode |= RTL8125_LED_CTRL_ACT;
++
++      return rtl8125_set_led_mode(tp, ldev->index, mode);
++}
++
++static int rtl8125_led_hw_control_get(struct led_classdev *led_cdev,
++                                    unsigned long *flags)
++{
++      struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
++      struct rtl8169_private *tp = netdev_priv(ldev->ndev);
++      int mode;
++
++      mode = rtl8125_get_led_mode(tp, ldev->index);
++      if (mode < 0)
++              return mode;
++
++      if (mode & RTL8125_LED_CTRL_LINK_10)
++              *flags |= BIT(TRIGGER_NETDEV_LINK_10);
++      if (mode & RTL8125_LED_CTRL_LINK_100)
++              *flags |= BIT(TRIGGER_NETDEV_LINK_100);
++      if (mode & RTL8125_LED_CTRL_LINK_1000)
++              *flags |= BIT(TRIGGER_NETDEV_LINK_1000);
++      if (mode & RTL8125_LED_CTRL_LINK_2500)
++              *flags |= BIT(TRIGGER_NETDEV_LINK_2500);
++      if (mode & RTL8125_LED_CTRL_ACT)
++              *flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
++
++      return 0;
++}
++
++static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev,
++                                 struct net_device *ndev, int index)
++{
++      struct rtl8169_private *tp = netdev_priv(ndev);
++      struct led_classdev *led_cdev = &ldev->led;
++      char led_name[LED_MAX_NAME_SIZE];
++
++      ldev->ndev = ndev;
++      ldev->index = index;
++
++      r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);
++      led_cdev->name = led_name;
++      led_cdev->hw_control_trigger = "netdev";
++      led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
++      led_cdev->hw_control_is_supported = rtl8125_led_hw_control_is_supported;
++      led_cdev->hw_control_set = rtl8125_led_hw_control_set;
++      led_cdev->hw_control_get = rtl8125_led_hw_control_get;
++      led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
++
++      /* ignore errors */
++      devm_led_classdev_register(&ndev->dev, led_cdev);
++}
++
++void rtl8125_init_leds(struct net_device *ndev)
++{
++      /* bind resource mgmt to netdev */
++      struct device *dev = &ndev->dev;
++      struct r8169_led_classdev *leds;
++      int i;
++
++      leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
++      if (!leds)
++              return;
++
++      for (i = 0; i < RTL8125_NUM_LEDS; i++)
++              rtl8125_setup_led_ldev(leds + i, ndev, i);
++}
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -330,17 +330,23 @@ enum rtl8168_registers {
+ };
+ enum rtl8125_registers {
++      LEDSEL0                 = 0x18,
+       INT_CFG0_8125           = 0x34,
+ #define INT_CFG0_ENABLE_8125          BIT(0)
+ #define INT_CFG0_CLKREQEN             BIT(3)
+       IntrMask_8125           = 0x38,
+       IntrStatus_8125         = 0x3c,
+       INT_CFG1_8125           = 0x7a,
++      LEDSEL2                 = 0x84,
++      LEDSEL1                 = 0x86,
+       TxPoll_8125             = 0x90,
++      LEDSEL3                 = 0x96,
+       MAC0_BKP                = 0x19e0,
+       EEE_TXIDLE_TIMER_8125   = 0x6048,
+ };
++#define LEDSEL_MASK_8125      0x23f
++
+ #define RX_VLAN_INNER_8125    BIT(22)
+ #define RX_VLAN_OUTER_8125    BIT(23)
+ #define RX_VLAN_8125          (RX_VLAN_INNER_8125 | RX_VLAN_OUTER_8125)
+@@ -857,6 +863,51 @@ int rtl8168_get_led_mode(struct rtl8169_
+       return ret;
+ }
++static int rtl8125_get_led_reg(int index)
++{
++      static const int led_regs[] = { LEDSEL0, LEDSEL1, LEDSEL2, LEDSEL3 };
++
++      return led_regs[index];
++}
++
++int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode)
++{
++      int reg = rtl8125_get_led_reg(index);
++      struct device *dev = tp_to_dev(tp);
++      int ret;
++      u16 val;
++
++      ret = pm_runtime_resume_and_get(dev);
++      if (ret < 0)
++              return ret;
++
++      mutex_lock(&tp->led_lock);
++      val = RTL_R16(tp, reg) & ~LEDSEL_MASK_8125;
++      RTL_W16(tp, reg, val | mode);
++      mutex_unlock(&tp->led_lock);
++
++      pm_runtime_put_sync(dev);
++
++      return 0;
++}
++
++int rtl8125_get_led_mode(struct rtl8169_private *tp, int index)
++{
++      int reg = rtl8125_get_led_reg(index);
++      struct device *dev = tp_to_dev(tp);
++      int ret;
++
++      ret = pm_runtime_resume_and_get(dev);
++      if (ret < 0)
++              return ret;
++
++      ret = RTL_R16(tp, reg);
++
++      pm_runtime_put_sync(dev);
++
++      return ret;
++}
++
+ void r8169_get_led_name(struct rtl8169_private *tp, int idx,
+                       char *buf, int buf_len)
+ {
+@@ -5442,10 +5493,12 @@ static int rtl_init_one(struct pci_dev *
+       if (rc)
+               return rc;
+-      if (IS_ENABLED(CONFIG_R8169_LEDS) &&
+-          tp->mac_version > RTL_GIGA_MAC_VER_06 &&
+-          tp->mac_version < RTL_GIGA_MAC_VER_61)
+-              rtl8168_init_leds(dev);
++      if (IS_ENABLED(CONFIG_R8169_LEDS)) {
++              if (rtl_is_8125(tp))
++                      rtl8125_init_leds(dev);
++              else if (tp->mac_version > RTL_GIGA_MAC_VER_06)
++                      rtl8168_init_leds(dev);
++      }
+       netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
+                   rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq);
diff --git a/target/linux/generic/backport-6.6/780-12-v6.9-r8169-add-generic-rtl_set_eee_txidle_timer-function.patch b/target/linux/generic/backport-6.6/780-12-v6.9-r8169-add-generic-rtl_set_eee_txidle_timer-function.patch
new file mode 100644 (file)
index 0000000..5f762f7
--- /dev/null
@@ -0,0 +1,91 @@
+From 2ce30993831041b9dcd31eb12896be6611e8b7e2 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 12 Feb 2024 19:57:46 +0100
+Subject: [PATCH] r8169: add generic rtl_set_eee_txidle_timer function
+
+Add a generic setter for the EEE tx idle timer and use it with all
+RTL8125/RTL8126 chip versions, in line with the vendor driver.
+This prepares for adding EEE tx idle timer support for additional
+chip versions.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/39beed72-0dc4-4c45-8899-b72c43ab62a7@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 34 +++++++++++++----------
+ 1 file changed, 20 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -646,6 +646,7 @@ struct rtl8169_private {
+       struct page *Rx_databuff[NUM_RX_DESC];  /* Rx data buffers */
+       struct ring_info tx_skb[NUM_TX_DESC];   /* Tx data buffers */
+       u16 cp_cmd;
++      u16 tx_lpi_timer;
+       u32 irq_mask;
+       int irq;
+       struct clk *clk;
+@@ -2081,6 +2082,22 @@ static int rtl_set_coalesce(struct net_d
+       return 0;
+ }
++static void rtl_set_eee_txidle_timer(struct rtl8169_private *tp)
++{
++      unsigned int timer_val = READ_ONCE(tp->dev->mtu) + ETH_HLEN + 0x20;
++
++      switch (tp->mac_version) {
++      case RTL_GIGA_MAC_VER_61:
++      case RTL_GIGA_MAC_VER_63:
++      case RTL_GIGA_MAC_VER_65:
++              tp->tx_lpi_timer = timer_val;
++              RTL_W16(tp, EEE_TXIDLE_TIMER_8125, timer_val);
++              break;
++      default:
++              break;
++      }
++}
++
+ static int rtl8169_get_eee(struct net_device *dev, struct ethtool_eee *data)
+ {
+       struct rtl8169_private *tp = netdev_priv(dev);
+@@ -2339,14 +2356,8 @@ static void rtl8125a_config_eee_mac(stru
+       r8168_mac_ocp_modify(tp, 0xeb62, 0, BIT(2) | BIT(1));
+ }
+-static void rtl8125_set_eee_txidle_timer(struct rtl8169_private *tp)
+-{
+-      RTL_W16(tp, EEE_TXIDLE_TIMER_8125, tp->dev->mtu + ETH_HLEN + 0x20);
+-}
+-
+ static void rtl8125b_config_eee_mac(struct rtl8169_private *tp)
+ {
+-      rtl8125_set_eee_txidle_timer(tp);
+       r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0));
+ }
+@@ -3879,6 +3890,8 @@ static void rtl_hw_start(struct  rtl8169
+       rtl_hw_aspm_clkreq_enable(tp, false);
+       RTL_W16(tp, CPlusCmd, tp->cp_cmd);
++      rtl_set_eee_txidle_timer(tp);
++
+       if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
+               rtl_hw_start_8169(tp);
+       else if (rtl_is_8125(tp))
+@@ -3912,14 +3925,7 @@ static int rtl8169_change_mtu(struct net
+       dev->mtu = new_mtu;
+       netdev_update_features(dev);
+       rtl_jumbo_config(tp);
+-
+-      switch (tp->mac_version) {
+-      case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
+-              rtl8125_set_eee_txidle_timer(tp);
+-              break;
+-      default:
+-              break;
+-      }
++      rtl_set_eee_txidle_timer(tp);
+       return 0;
+ }
diff --git a/target/linux/generic/backport-6.6/780-13-v6.9-r8169-support-setting-the-EEE-tx-idle-timer-on-RTL81.patch b/target/linux/generic/backport-6.6/780-13-v6.9-r8169-support-setting-the-EEE-tx-idle-timer-on-RTL81.patch
new file mode 100644 (file)
index 0000000..8669dd2
--- /dev/null
@@ -0,0 +1,30 @@
+From 57d2d2c8f132c830565058a5cdd8138350e068ec Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 12 Feb 2024 19:58:47 +0100
+Subject: [PATCH] r8169: support setting the EEE tx idle timer on
+ RTL8168h
+
+Support setting the EEE tx idle timer also on RTL8168h.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/cfb69ec9-24c4-4aad-9909-fdae3088add4@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -2087,6 +2087,11 @@ static void rtl_set_eee_txidle_timer(str
+       unsigned int timer_val = READ_ONCE(tp->dev->mtu) + ETH_HLEN + 0x20;
+       switch (tp->mac_version) {
++      case RTL_GIGA_MAC_VER_46:
++      case RTL_GIGA_MAC_VER_48:
++              tp->tx_lpi_timer = timer_val;
++              r8168_mac_ocp_write(tp, 0xe048, timer_val);
++              break;
+       case RTL_GIGA_MAC_VER_61:
+       case RTL_GIGA_MAC_VER_63:
+       case RTL_GIGA_MAC_VER_65:
diff --git a/target/linux/generic/backport-6.6/780-14-v6.9-r8169-add-support-for-returning-tx_lpi_timer-in-etht.patch b/target/linux/generic/backport-6.6/780-14-v6.9-r8169-add-support-for-returning-tx_lpi_timer-in-etht.patch
new file mode 100644 (file)
index 0000000..1a27bd1
--- /dev/null
@@ -0,0 +1,55 @@
+From 9c50139727265c088f936e496777bf588850e9f1 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 12 Feb 2024 19:59:26 +0100
+Subject: [PATCH] r8169: add support for returning tx_lpi_timer in
+ ethtool get_eee
+
+Add support for returning the tx_lpi_timer value to userspace.
+This is supported by few chip versions only: RTL8168h/RTL8125/RTL8126
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/4eee9c34-c5d6-4c96-9b05-455896dea59a@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 22 +++++++++++++++++++++-
+ 1 file changed, 21 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -2103,14 +2103,34 @@ static void rtl_set_eee_txidle_timer(str
+       }
+ }
++static unsigned int r8169_get_tx_lpi_timer_us(struct rtl8169_private *tp)
++{
++      unsigned int speed = tp->phydev->speed;
++      unsigned int timer = tp->tx_lpi_timer;
++
++      if (!timer || speed == SPEED_UNKNOWN)
++              return 0;
++
++      /* tx_lpi_timer value is in bytes */
++      return DIV_ROUND_CLOSEST(timer * BITS_PER_BYTE, speed);
++}
++
+ static int rtl8169_get_eee(struct net_device *dev, struct ethtool_eee *data)
+ {
+       struct rtl8169_private *tp = netdev_priv(dev);
++      int ret;
+       if (!rtl_supports_eee(tp))
+               return -EOPNOTSUPP;
+-      return phy_ethtool_get_eee(tp->phydev, data);
++      ret = phy_ethtool_get_eee(tp->phydev, data);
++      if (ret)
++              return ret;
++
++      data->tx_lpi_timer = r8169_get_tx_lpi_timer_us(tp);
++      data->tx_lpi_enabled = data->tx_lpi_timer ? data->eee_enabled : false;
++
++      return 0;
+ }
+ static int rtl8169_set_eee(struct net_device *dev, struct ethtool_eee *data)
diff --git a/target/linux/generic/backport-6.6/780-15-v6.9-r8169-add-MODULE_FIRMWARE-entry-for-RTL8126A.patch b/target/linux/generic/backport-6.6/780-15-v6.9-r8169-add-MODULE_FIRMWARE-entry-for-RTL8126A.patch
new file mode 100644 (file)
index 0000000..da33372
--- /dev/null
@@ -0,0 +1,25 @@
+From f4d3e595c0000ce39dec7e4799ea42ce42ab6867 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Sat, 17 Feb 2024 15:48:23 +0100
+Subject: [PATCH] r8169: add MODULE_FIRMWARE entry for RTL8126A
+
+Add the missing MODULE_FIRMWARE entry for RTL8126A.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://lore.kernel.org/r/47ef79d2-59c4-4d44-9595-366c70c4ad87@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -703,6 +703,7 @@ MODULE_FIRMWARE(FIRMWARE_8168FP_3);
+ MODULE_FIRMWARE(FIRMWARE_8107E_2);
+ MODULE_FIRMWARE(FIRMWARE_8125A_3);
+ MODULE_FIRMWARE(FIRMWARE_8125B_2);
++MODULE_FIRMWARE(FIRMWARE_8126A_2);
+ static inline struct device *tp_to_dev(struct rtl8169_private *tp)
+ {
diff --git a/target/linux/generic/backport-6.6/780-16-v6.9-r8169-fix-LED-related-deadlock-on-module-removal.patch b/target/linux/generic/backport-6.6/780-16-v6.9-r8169-fix-LED-related-deadlock-on-module-removal.patch
new file mode 100644 (file)
index 0000000..cb031d8
--- /dev/null
@@ -0,0 +1,147 @@
+From 19fa4f2a85d777a8052e869c1b892a2f7556569d Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 8 Apr 2024 20:47:40 +0200
+Subject: [PATCH] r8169: fix LED-related deadlock on module removal
+
+Binding devm_led_classdev_register() to the netdev is problematic
+because on module removal we get a RTNL-related deadlock. Fix this
+by avoiding the device-managed LED functions.
+
+Note: We can safely call led_classdev_unregister() for a LED even
+if registering it failed, because led_classdev_unregister() detects
+this and is a no-op in this case.
+
+Fixes: 18764b883e15 ("r8169: add support for LED's on RTL8168/RTL8101")
+Cc: stable@vger.kernel.org
+Reported-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/realtek/r8169.h      |  6 ++--
+ drivers/net/ethernet/realtek/r8169_leds.c | 35 +++++++++++++++--------
+ drivers/net/ethernet/realtek/r8169_main.c |  8 ++++--
+ 3 files changed, 33 insertions(+), 16 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169.h
++++ b/drivers/net/ethernet/realtek/r8169.h
+@@ -73,6 +73,7 @@ enum mac_version {
+ };
+ struct rtl8169_private;
++struct r8169_led_classdev;
+ void r8169_apply_firmware(struct rtl8169_private *tp);
+ u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp);
+@@ -84,7 +85,8 @@ void r8169_get_led_name(struct rtl8169_p
+                       char *buf, int buf_len);
+ int rtl8168_get_led_mode(struct rtl8169_private *tp);
+ int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
+-void rtl8168_init_leds(struct net_device *ndev);
++struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev);
+ int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
+ int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
+-void rtl8125_init_leds(struct net_device *ndev);
++struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev);
++void r8169_remove_leds(struct r8169_led_classdev *leds);
+--- a/drivers/net/ethernet/realtek/r8169_leds.c
++++ b/drivers/net/ethernet/realtek/r8169_leds.c
+@@ -147,22 +147,22 @@ static void rtl8168_setup_ldev(struct r8
+       led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
+       /* ignore errors */
+-      devm_led_classdev_register(&ndev->dev, led_cdev);
++      led_classdev_register(&ndev->dev, led_cdev);
+ }
+-void rtl8168_init_leds(struct net_device *ndev)
++struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev)
+ {
+-      /* bind resource mgmt to netdev */
+-      struct device *dev = &ndev->dev;
+       struct r8169_led_classdev *leds;
+       int i;
+-      leds = devm_kcalloc(dev, RTL8168_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
++      leds = kcalloc(RTL8168_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
+       if (!leds)
+-              return;
++              return NULL;
+       for (i = 0; i < RTL8168_NUM_LEDS; i++)
+               rtl8168_setup_ldev(leds + i, ndev, i);
++
++      return leds;
+ }
+ static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,
+@@ -246,20 +246,31 @@ static void rtl8125_setup_led_ldev(struc
+       led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
+       /* ignore errors */
+-      devm_led_classdev_register(&ndev->dev, led_cdev);
++      led_classdev_register(&ndev->dev, led_cdev);
+ }
+-void rtl8125_init_leds(struct net_device *ndev)
++struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev)
+ {
+-      /* bind resource mgmt to netdev */
+-      struct device *dev = &ndev->dev;
+       struct r8169_led_classdev *leds;
+       int i;
+-      leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
++      leds = kcalloc(RTL8125_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
+       if (!leds)
+-              return;
++              return NULL;
+       for (i = 0; i < RTL8125_NUM_LEDS; i++)
+               rtl8125_setup_led_ldev(leds + i, ndev, i);
++
++      return leds;
++}
++
++void r8169_remove_leds(struct r8169_led_classdev *leds)
++{
++      if (!leds)
++              return;
++
++      for (struct r8169_led_classdev *l = leds; l->ndev; l++)
++              led_classdev_unregister(&l->led);
++
++      kfree(leds);
+ }
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -674,6 +674,8 @@ struct rtl8169_private {
+       const char *fw_name;
+       struct rtl_fw *rtl_fw;
++      struct r8169_led_classdev *leds;
++
+       u32 ocp_base;
+ };
+@@ -5069,6 +5071,8 @@ static void rtl_remove_one(struct pci_de
+       cancel_work_sync(&tp->wk.work);
++      r8169_remove_leds(tp->leds);
++
+       unregister_netdev(tp->dev);
+       if (tp->dash_type != RTL_DASH_NONE)
+@@ -5527,9 +5531,9 @@ static int rtl_init_one(struct pci_dev *
+       if (IS_ENABLED(CONFIG_R8169_LEDS)) {
+               if (rtl_is_8125(tp))
+-                      rtl8125_init_leds(dev);
++                      tp->leds = rtl8125_init_leds(dev);
+               else if (tp->mac_version > RTL_GIGA_MAC_VER_06)
+-                      rtl8168_init_leds(dev);
++                      tp->leds = rtl8168_init_leds(dev);
+       }
+       netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
diff --git a/target/linux/generic/backport-6.6/780-17-v6.9-r8169-add-missing-conditional-compiling-for-call-to-.patch b/target/linux/generic/backport-6.6/780-17-v6.9-r8169-add-missing-conditional-compiling-for-call-to-.patch
new file mode 100644 (file)
index 0000000..0412c91
--- /dev/null
@@ -0,0 +1,31 @@
+From 97e176fcbbf3c0f2bd410c9b241177c051f57176 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Wed, 10 Apr 2024 15:11:28 +0200
+Subject: [PATCH] r8169: add missing conditional compiling for call to
+ r8169_remove_leds
+
+Add missing dependency on CONFIG_R8169_LEDS. As-is a link error occurs
+if config option CONFIG_R8169_LEDS isn't enabled.
+
+Fixes: 19fa4f2a85d7 ("r8169: fix LED-related deadlock on module removal")
+Reported-by: Venkat Rao Bagalkote <venkat88@linux.vnet.ibm.com>
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Tested-By: Venkat Rao Bagalkote <venkat88@linux.vnet.ibm.com>
+Link: https://lore.kernel.org/r/d080038c-eb6b-45ac-9237-b8c1cdd7870f@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -5071,7 +5071,8 @@ static void rtl_remove_one(struct pci_de
+       cancel_work_sync(&tp->wk.work);
+-      r8169_remove_leds(tp->leds);
++      if (IS_ENABLED(CONFIG_R8169_LEDS))
++              r8169_remove_leds(tp->leds);
+       unregister_netdev(tp->dev);
diff --git a/target/linux/generic/backport-6.6/780-18-v6.10-r8169-add-support-for-RTL8168M.patch b/target/linux/generic/backport-6.6/780-18-v6.10-r8169-add-support-for-RTL8168M.patch
new file mode 100644 (file)
index 0000000..95455ee
--- /dev/null
@@ -0,0 +1,30 @@
+From 39f59c72ad3a1eaab9a60f0671bc94d2bc826d21 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Sun, 7 Apr 2024 23:19:25 +0200
+Subject: [PATCH] r8169: add support for RTL8168M
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+A user reported an unknown chip version. According to the r8168 vendor
+driver it's called RTL8168M, but handling is identical to RTL8168H.
+So let's simply treat it as RTL8168H.
+
+Tested-by: Евгений <octobergun@gmail.com>
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -2257,6 +2257,8 @@ static enum mac_version rtl8169_get_mac_
+                * the wild. Let's disable detection.
+                * { 0x7cf, 0x540,      RTL_GIGA_MAC_VER_45 },
+                */
++              /* Realtek calls it RTL8168M, but it's handled like RTL8168H */
++              { 0x7cf, 0x6c0, RTL_GIGA_MAC_VER_46 },
+               /* 8168G family. */
+               { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44 },
diff --git a/target/linux/generic/backport-6.6/780-19-v6.10-net-annotate-writes-on-dev-mtu-from-ndo_change_mtu.patch b/target/linux/generic/backport-6.6/780-19-v6.10-net-annotate-writes-on-dev-mtu-from-ndo_change_mtu.patch
new file mode 100644 (file)
index 0000000..4c05976
--- /dev/null
@@ -0,0 +1,60 @@
+From 1eb2cded45b35816085c1f962933c187d970f9dc Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Mon, 6 May 2024 10:28:12 +0000
+Subject: [PATCH] net: annotate writes on dev->mtu from ndo_change_mtu()
+
+Simon reported that ndo_change_mtu() methods were never
+updated to use WRITE_ONCE(dev->mtu, new_mtu) as hinted
+in commit 501a90c94510 ("inet: protect against too small
+mtu values.")
+
+We read dev->mtu without holding RTNL in many places,
+with READ_ONCE() annotations.
+
+It is time to take care of ndo_change_mtu() methods
+to use corresponding WRITE_ONCE()
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Reported-by: Simon Horman <horms@kernel.org>
+Closes: https://lore.kernel.org/netdev/20240505144608.GB67882@kernel.org/
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Acked-by: Shannon Nelson <shannon.nelson@amd.com>
+Link: https://lore.kernel.org/r/20240506102812.3025432-1-edumazet@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/8139cp.c     | 4 ++--
+ drivers/net/ethernet/realtek/r8169_main.c | 2 +-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/8139cp.c
++++ b/drivers/net/ethernet/realtek/8139cp.c
+@@ -1277,14 +1277,14 @@ static int cp_change_mtu(struct net_devi
+       /* if network interface not up, no need for complexity */
+       if (!netif_running(dev)) {
+-              dev->mtu = new_mtu;
++              WRITE_ONCE(dev->mtu, new_mtu);
+               cp_set_rxbufsize(cp);   /* set new rx buf size */
+               return 0;
+       }
+       /* network IS up, close it, reset MTU, and come up again. */
+       cp_close(dev);
+-      dev->mtu = new_mtu;
++      WRITE_ONCE(dev->mtu, new_mtu);
+       cp_set_rxbufsize(cp);
+       return cp_open(dev);
+ }
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -3952,7 +3952,7 @@ static int rtl8169_change_mtu(struct net
+ {
+       struct rtl8169_private *tp = netdev_priv(dev);
+-      dev->mtu = new_mtu;
++      WRITE_ONCE(dev->mtu, new_mtu);
+       netdev_update_features(dev);
+       rtl_jumbo_config(tp);
+       rtl_set_eee_txidle_timer(tp);
diff --git a/target/linux/generic/backport-6.6/780-20-v6.11-r8169-disable-interrupt-source-RxOverflow.patch b/target/linux/generic/backport-6.6/780-20-v6.11-r8169-disable-interrupt-source-RxOverflow.patch
new file mode 100644 (file)
index 0000000..fc10a3f
--- /dev/null
@@ -0,0 +1,34 @@
+From 6994520a332887f1688464f250c9ec8002a89a8e Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 27 May 2024 21:16:56 +0200
+Subject: [PATCH] r8169: disable interrupt source RxOverflow
+
+Vendor driver calls this bit RxDescUnavail. All we do in the interrupt
+handler in this case is scheduling NAPI. If we should be out of
+RX descriptors, then NAPI is scheduled anyway. Therefore remove this
+interrupt source. Tested on RTL8168h.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Sunil Goutham <sgoutham@marvell.com>
+Link: https://lore.kernel.org/r/9b2054b2-0548-4f48-bf91-b646572093b4@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -5112,12 +5112,10 @@ static void rtl_set_irq_mask(struct rtl8
+       tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;
+       if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
+-              tp->irq_mask |= SYSErr | RxOverflow | RxFIFOOver;
++              tp->irq_mask |= SYSErr | RxFIFOOver;
+       else if (tp->mac_version == RTL_GIGA_MAC_VER_11)
+               /* special workaround needed */
+               tp->irq_mask |= RxFIFOOver;
+-      else
+-              tp->irq_mask |= RxOverflow;
+ }
+ static int rtl_alloc_irq(struct rtl8169_private *tp)
diff --git a/target/linux/generic/backport-6.6/780-21-v6.11-r8169-remove-detection-of-chip-version-11-early-RTL8.patch b/target/linux/generic/backport-6.6/780-21-v6.11-r8169-remove-detection-of-chip-version-11-early-RTL8.patch
new file mode 100644 (file)
index 0000000..5e9e765
--- /dev/null
@@ -0,0 +1,32 @@
+From 982300c115d229565d7af8e8b38aa1ee7bb1f5bd Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 27 May 2024 21:20:16 +0200
+Subject: [PATCH] r8169: remove detection of chip version 11 (early
+ RTL8168b)
+
+This early RTL8168b version was the first PCIe chip version, and it's
+quite quirky. Last sign of life is from more than 15 yrs ago.
+Let's remove detection of this chip version, we'll see whether anybody
+complains. If not, support for this chip version can be removed a few
+kernel versions later.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Link: https://lore.kernel.org/r/875cdcf4-843c-420a-ad5d-417447b68572@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -2302,7 +2302,9 @@ static enum mac_version rtl8169_get_mac_
+               /* 8168B family. */
+               { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 },
+-              { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 },
++              /* This one is very old and rare, let's see if anybody complains.
++               * { 0x7c8, 0x300,      RTL_GIGA_MAC_VER_11 },
++               */
+               /* 8101 family. */
+               { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39 },
diff --git a/target/linux/generic/backport-6.6/780-22-v6.12-r8169-add-support-for-RTL8126A-rev.b.patch b/target/linux/generic/backport-6.6/780-22-v6.12-r8169-add-support-for-RTL8126A-rev.b.patch
new file mode 100644 (file)
index 0000000..a825627
--- /dev/null
@@ -0,0 +1,245 @@
+From 69cb89981c7a181d857b634c0740e914d5df79ea Mon Sep 17 00:00:00 2001
+From: ChunHao Lin <hau@realtek.com>
+Date: Fri, 30 Aug 2024 10:18:10 +0800
+Subject: [PATCH] r8169: add support for RTL8126A rev.b
+
+Add support for RTL8126A rev.b. Its XID is 0x64a. It is basically
+based on the one with XID 0x649, but with different firmware file.
+
+Signed-off-by: ChunHao Lin <hau@realtek.com>
+Reviewed-by: Heiner Kallweit <hkallweit1@gmail.com>
+Link: https://patch.msgid.link/20240830021810.11993-1-hau@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/realtek/r8169.h          |  1 +
+ drivers/net/ethernet/realtek/r8169_main.c     | 42 ++++++++++++-------
+ .../net/ethernet/realtek/r8169_phy_config.c   |  1 +
+ 3 files changed, 29 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/ethernet/realtek/r8169.h
++++ b/drivers/net/ethernet/realtek/r8169.h
+@@ -69,6 +69,7 @@ enum mac_version {
+       RTL_GIGA_MAC_VER_61,
+       RTL_GIGA_MAC_VER_63,
+       RTL_GIGA_MAC_VER_65,
++      RTL_GIGA_MAC_VER_66,
+       RTL_GIGA_MAC_NONE
+ };
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -56,6 +56,7 @@
+ #define FIRMWARE_8125A_3      "rtl_nic/rtl8125a-3.fw"
+ #define FIRMWARE_8125B_2      "rtl_nic/rtl8125b-2.fw"
+ #define FIRMWARE_8126A_2      "rtl_nic/rtl8126a-2.fw"
++#define FIRMWARE_8126A_3      "rtl_nic/rtl8126a-3.fw"
+ #define TX_DMA_BURST  7       /* Maximum PCI burst, '7' is unlimited */
+ #define InterFrameGap 0x03    /* 3 means InterFrameGap = the shortest one */
+@@ -138,6 +139,7 @@ static const struct {
+       /* reserve 62 for CFG_METHOD_4 in the vendor driver */
+       [RTL_GIGA_MAC_VER_63] = {"RTL8125B",            FIRMWARE_8125B_2},
+       [RTL_GIGA_MAC_VER_65] = {"RTL8126A",            FIRMWARE_8126A_2},
++      [RTL_GIGA_MAC_VER_66] = {"RTL8126A",            FIRMWARE_8126A_3},
+ };
+ static const struct pci_device_id rtl8169_pci_tbl[] = {
+@@ -1228,7 +1230,7 @@ static void rtl_writephy(struct rtl8169_
+       case RTL_GIGA_MAC_VER_31:
+               r8168dp_2_mdio_write(tp, location, val);
+               break;
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
+               r8168g_mdio_write(tp, location, val);
+               break;
+       default:
+@@ -1243,7 +1245,7 @@ static int rtl_readphy(struct rtl8169_pr
+       case RTL_GIGA_MAC_VER_28:
+       case RTL_GIGA_MAC_VER_31:
+               return r8168dp_2_mdio_read(tp, location);
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
+               return r8168g_mdio_read(tp, location);
+       default:
+               return r8169_mdio_read(tp, location);
+@@ -1452,7 +1454,7 @@ static void rtl_set_d3_pll_down(struct r
+       case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26:
+       case RTL_GIGA_MAC_VER_29 ... RTL_GIGA_MAC_VER_30:
+       case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37:
+-      case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_66:
+               if (enable)
+                       RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN);
+               else
+@@ -1619,7 +1621,7 @@ static void __rtl8169_set_wol(struct rtl
+               break;
+       case RTL_GIGA_MAC_VER_34:
+       case RTL_GIGA_MAC_VER_37:
+-      case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_66:
+               if (wolopts)
+                       rtl_mod_config2(tp, 0, PME_SIGNAL);
+               else
+@@ -2098,6 +2100,7 @@ static void rtl_set_eee_txidle_timer(str
+       case RTL_GIGA_MAC_VER_61:
+       case RTL_GIGA_MAC_VER_63:
+       case RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_66:
+               tp->tx_lpi_timer = timer_val;
+               RTL_W16(tp, EEE_TXIDLE_TIMER_8125, timer_val);
+               break;
+@@ -2227,6 +2230,7 @@ static enum mac_version rtl8169_get_mac_
+               enum mac_version ver;
+       } mac_info[] = {
+               /* 8126A family. */
++              { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_66 },
+               { 0x7cf, 0x649, RTL_GIGA_MAC_VER_65 },
+               /* 8125B family. */
+@@ -2498,6 +2502,7 @@ static void rtl_init_rxcfg(struct rtl816
+               break;
+       case RTL_GIGA_MAC_VER_63:
+       case RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_66:
+               RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST |
+                       RX_PAUSE_SLOT_ON);
+               break;
+@@ -2684,7 +2689,7 @@ static void rtl_wait_txrx_fifo_empty(str
+       case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61:
+               rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
+               break;
+-      case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_66:
+               RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
+               rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42);
+               rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42);
+@@ -2927,7 +2932,7 @@ static void rtl_enable_exit_l1(struct rt
+       case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38:
+               rtl_eri_set_bits(tp, 0xd4, 0x0c00);
+               break;
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
+               r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80);
+               break;
+       default:
+@@ -2941,7 +2946,7 @@ static void rtl_disable_exit_l1(struct r
+       case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38:
+               rtl_eri_clear_bits(tp, 0xd4, 0x1f00);
+               break;
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
+               r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0);
+               break;
+       default:
+@@ -2968,6 +2973,7 @@ static void rtl_hw_aspm_clkreq_enable(st
+               rtl_mod_config5(tp, 0, ASPM_en);
+               switch (tp->mac_version) {
+               case RTL_GIGA_MAC_VER_65:
++              case RTL_GIGA_MAC_VER_66:
+                       val8 = RTL_R8(tp, INT_CFG0_8125) | INT_CFG0_CLKREQEN;
+                       RTL_W8(tp, INT_CFG0_8125, val8);
+                       break;
+@@ -2978,7 +2984,7 @@ static void rtl_hw_aspm_clkreq_enable(st
+               switch (tp->mac_version) {
+               case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
+-              case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
++              case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66:
+                       /* reset ephy tx/rx disable timer */
+                       r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0);
+                       /* chip can trigger L1.2 */
+@@ -2990,7 +2996,7 @@ static void rtl_hw_aspm_clkreq_enable(st
+       } else {
+               switch (tp->mac_version) {
+               case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48:
+-              case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
++              case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66:
+                       r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0);
+                       break;
+               default:
+@@ -2999,6 +3005,7 @@ static void rtl_hw_aspm_clkreq_enable(st
+               switch (tp->mac_version) {
+               case RTL_GIGA_MAC_VER_65:
++              case RTL_GIGA_MAC_VER_66:
+                       val8 = RTL_R8(tp, INT_CFG0_8125) & ~INT_CFG0_CLKREQEN;
+                       RTL_W8(tp, INT_CFG0_8125, val8);
+                       break;
+@@ -3718,10 +3725,12 @@ static void rtl_hw_start_8125_common(str
+       /* disable new tx descriptor format */
+       r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000);
+-      if (tp->mac_version == RTL_GIGA_MAC_VER_65)
++      if (tp->mac_version == RTL_GIGA_MAC_VER_65 ||
++          tp->mac_version == RTL_GIGA_MAC_VER_66)
+               RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02);
+-      if (tp->mac_version == RTL_GIGA_MAC_VER_65)
++      if (tp->mac_version == RTL_GIGA_MAC_VER_65 ||
++          tp->mac_version == RTL_GIGA_MAC_VER_66)
+               r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400);
+       else if (tp->mac_version == RTL_GIGA_MAC_VER_63)
+               r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200);
+@@ -3739,7 +3748,8 @@ static void rtl_hw_start_8125_common(str
+       r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0030);
+       r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000);
+       r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001);
+-      if (tp->mac_version == RTL_GIGA_MAC_VER_65)
++      if (tp->mac_version == RTL_GIGA_MAC_VER_65 ||
++          tp->mac_version == RTL_GIGA_MAC_VER_66)
+               r8168_mac_ocp_modify(tp, 0xea1c, 0x0300, 0x0000);
+       else
+               r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000);
+@@ -3853,6 +3863,7 @@ static void rtl_hw_config(struct rtl8169
+               [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2,
+               [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b,
+               [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8126a,
++              [RTL_GIGA_MAC_VER_66] = rtl_hw_start_8126a,
+       };
+       if (hw_configs[tp->mac_version])
+@@ -3873,6 +3884,7 @@ static void rtl_hw_start_8125(struct rtl
+               break;
+       case RTL_GIGA_MAC_VER_63:
+       case RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_66:
+               for (i = 0xa00; i < 0xa80; i += 4)
+                       RTL_W32(tp, i, 0);
+               RTL_W16(tp, INT_CFG1_8125, 0x0000);
+@@ -4101,7 +4113,7 @@ static void rtl8169_cleanup(struct rtl81
+               RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
+               rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
+               break;
+-      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_66:
+               rtl_enable_rxdvgate(tp);
+               fsleep(2000);
+               break;
+@@ -4252,7 +4264,7 @@ static unsigned int rtl_quirk_packet_pad
+       switch (tp->mac_version) {
+       case RTL_GIGA_MAC_VER_34:
+-      case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66:
+               padto = max_t(unsigned int, padto, ETH_ZLEN);
+               break;
+       default:
+@@ -5288,7 +5300,7 @@ static void rtl_hw_initialize(struct rtl
+       case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48:
+               rtl_hw_init_8168g(tp);
+               break;
+-      case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_65:
++      case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66:
+               rtl_hw_init_8125(tp);
+               break;
+       default:
+--- a/drivers/net/ethernet/realtek/r8169_phy_config.c
++++ b/drivers/net/ethernet/realtek/r8169_phy_config.c
+@@ -1161,6 +1161,7 @@ void r8169_hw_phy_config(struct rtl8169_
+               [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config,
+               [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config,
+               [RTL_GIGA_MAC_VER_65] = rtl8126a_hw_phy_config,
++              [RTL_GIGA_MAC_VER_66] = rtl8126a_hw_phy_config,
+       };
+       if (phy_configs[ver])
diff --git a/target/linux/generic/backport-6.6/780-23-v6.12-r8169-add-missing-MODULE_FIRMWARE-entry-for-RTL8126A.patch b/target/linux/generic/backport-6.6/780-23-v6.12-r8169-add-missing-MODULE_FIRMWARE-entry-for-RTL8126A.patch
new file mode 100644 (file)
index 0000000..1423481
--- /dev/null
@@ -0,0 +1,25 @@
+From 3b067536daa4842adbf685accf47c899a26367d3 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Wed, 18 Sep 2024 20:45:15 +0200
+Subject: [PATCH] r8169: add missing MODULE_FIRMWARE entry for RTL8126A rev.b
+
+Add a missing MODULE_FIRMWARE entry.
+
+Fixes: 69cb89981c7a ("r8169: add support for RTL8126A rev.b")
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Link: https://patch.msgid.link/bb307611-d129-43f5-a7ff-bdb6b4044fce@gmail.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/realtek/r8169_main.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/ethernet/realtek/r8169_main.c
++++ b/drivers/net/ethernet/realtek/r8169_main.c
+@@ -708,6 +708,7 @@ MODULE_FIRMWARE(FIRMWARE_8107E_2);
+ MODULE_FIRMWARE(FIRMWARE_8125A_3);
+ MODULE_FIRMWARE(FIRMWARE_8125B_2);
+ MODULE_FIRMWARE(FIRMWARE_8126A_2);
++MODULE_FIRMWARE(FIRMWARE_8126A_3);
+ static inline struct device *tp_to_dev(struct rtl8169_private *tp)
+ {
diff --git a/target/linux/generic/backport-6.6/781-01-v6.9-net-phy-realtek-add-support-for-RTL8126A-integrated-.patch b/target/linux/generic/backport-6.6/781-01-v6.9-net-phy-realtek-add-support-for-RTL8126A-integrated-.patch
new file mode 100644 (file)
index 0000000..723742e
--- /dev/null
@@ -0,0 +1,40 @@
+From 5befa3728b855e9f75b29bb0069a1ca7f5bab2f7 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Wed, 31 Jan 2024 21:24:29 +0100
+Subject: [PATCH] net: phy: realtek: add support for RTL8126A-integrated 5Gbps
+ PHY
+
+A user reported that first consumer mainboards show up with a RTL8126A
+5Gbps MAC/PHY. This adds support for the integrated PHY, which is also
+available stand-alone. From a PHY driver perspective it's treated the
+same as the 2.5Gbps PHY's, we just have to support the new PHY ID.
+
+Reported-by: Joe Salmeri <jmscdba@gmail.com>
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Tested-by: Joe Salmeri <jmscdba@gmail.com>
+Link: https://lore.kernel.org/r/0c8e67ea-6505-43d1-bd51-94e7ecd6e222@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/realtek.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1050,6 +1050,16 @@ static struct phy_driver realtek_drvs[]
+               .read_page      = rtl821x_read_page,
+               .write_page     = rtl821x_write_page,
+       }, {
++              PHY_ID_MATCH_EXACT(0x001cc862),
++              .name           = "RTL8251B 5Gbps PHY",
++              .get_features   = rtl822x_get_features,
++              .config_aneg    = rtl822x_config_aneg,
++              .read_status    = rtl822x_read_status,
++              .suspend        = genphy_suspend,
++              .resume         = rtlgen_resume,
++              .read_page      = rtl821x_read_page,
++              .write_page     = rtl821x_write_page,
++      }, {
+               PHY_ID_MATCH_EXACT(0x001cc961),
+               .name           = "RTL8366RB Gigabit Ethernet",
+               .config_init    = &rtl8366rb_config_init,
diff --git a/target/linux/generic/backport-6.6/781-02-v6.9-net-phy-realtek-use-generic-MDIO-constants.patch b/target/linux/generic/backport-6.6/781-02-v6.9-net-phy-realtek-use-generic-MDIO-constants.patch
new file mode 100644 (file)
index 0000000..6672aca
--- /dev/null
@@ -0,0 +1,93 @@
+From 2b9ec5dfb8255656ca731ab9d9bf59d94566d377 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Sun, 4 Feb 2024 15:17:53 +0100
+Subject: [PATCH] net: phy: realtek: use generic MDIO constants
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Drop the ad-hoc MDIO constants used in the driver and use generic
+constants instead.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/732a70d6-4191-4aae-8862-3716b062aa9e@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/realtek.c | 30 +++++++++++++-----------------
+ 1 file changed, 13 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -57,14 +57,6 @@
+ #define RTL8366RB_POWER_SAVE                  0x15
+ #define RTL8366RB_POWER_SAVE_ON                       BIT(12)
+-#define RTL_SUPPORTS_5000FULL                 BIT(14)
+-#define RTL_SUPPORTS_2500FULL                 BIT(13)
+-#define RTL_SUPPORTS_10000FULL                        BIT(0)
+-#define RTL_ADV_2500FULL                      BIT(7)
+-#define RTL_LPADV_10000FULL                   BIT(11)
+-#define RTL_LPADV_5000FULL                    BIT(6)
+-#define RTL_LPADV_2500FULL                    BIT(5)
+-
+ #define RTL9000A_GINMR                                0x14
+ #define RTL9000A_GINMR_LINK_STATUS            BIT(4)
+@@ -676,11 +668,11 @@ static int rtl822x_get_features(struct p
+               return val;
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+-                       phydev->supported, val & RTL_SUPPORTS_2500FULL);
++                       phydev->supported, val & MDIO_PMA_SPEED_2_5G);
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+-                       phydev->supported, val & RTL_SUPPORTS_5000FULL);
++                       phydev->supported, val & MDIO_PMA_SPEED_5G);
+       linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+-                       phydev->supported, val & RTL_SUPPORTS_10000FULL);
++                       phydev->supported, val & MDIO_SPEED_10G);
+       return genphy_read_abilities(phydev);
+ }
+@@ -694,10 +686,11 @@ static int rtl822x_config_aneg(struct ph
+               if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+                                     phydev->advertising))
+-                      adv2500 = RTL_ADV_2500FULL;
++                      adv2500 = MDIO_AN_10GBT_CTRL_ADV2_5G;
+               ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
+-                                             RTL_ADV_2500FULL, adv2500);
++                                             MDIO_AN_10GBT_CTRL_ADV2_5G,
++                                             adv2500);
+               if (ret < 0)
+                       return ret;
+       }
+@@ -716,11 +709,14 @@ static int rtl822x_read_status(struct ph
+                       return lpadv;
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+-                      phydev->lp_advertising, lpadv & RTL_LPADV_10000FULL);
++                               phydev->lp_advertising,
++                               lpadv & MDIO_AN_10GBT_STAT_LP10G);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+-                      phydev->lp_advertising, lpadv & RTL_LPADV_5000FULL);
++                               phydev->lp_advertising,
++                               lpadv & MDIO_AN_10GBT_STAT_LP5G);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+-                      phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL);
++                               phydev->lp_advertising,
++                               lpadv & MDIO_AN_10GBT_STAT_LP2_5G);
+       }
+       ret = genphy_read_status(phydev);
+@@ -738,7 +734,7 @@ static bool rtlgen_supports_2_5gbps(stru
+       val = phy_read(phydev, 0x13);
+       phy_write(phydev, RTL821x_PAGE_SELECT, 0);
+-      return val >= 0 && val & RTL_SUPPORTS_2500FULL;
++      return val >= 0 && val & MDIO_PMA_SPEED_2_5G;
+ }
+ static int rtlgen_match_phy_device(struct phy_device *phydev)
diff --git a/target/linux/generic/backport-6.6/781-03-v6.9-net-phy-realtek-add-5Gbps-support-to-rtl822x_config_.patch b/target/linux/generic/backport-6.6/781-03-v6.9-net-phy-realtek-add-5Gbps-support-to-rtl822x_config_.patch
new file mode 100644 (file)
index 0000000..4b99a0e
--- /dev/null
@@ -0,0 +1,42 @@
+From db1bb7741ff29bf2cefcbc0ca567644e9ed1caa9 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Sun, 4 Feb 2024 15:18:50 +0100
+Subject: [PATCH] net: phy: realtek: add 5Gbps support to rtl822x_config_aneg()
+
+RTL8126 as an evolution of RTL8125 supports 5Gbps. rtl822x_config_aneg()
+is used by the PHY driver for the integrated PHY, therefore add 5Gbps
+support to it.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Link: https://lore.kernel.org/r/5644ab50-e3e9-477c-96db-05cd5bdc2563@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/realtek.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -682,15 +682,19 @@ static int rtl822x_config_aneg(struct ph
+       int ret = 0;
+       if (phydev->autoneg == AUTONEG_ENABLE) {
+-              u16 adv2500 = 0;
++              u16 adv = 0;
+               if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+                                     phydev->advertising))
+-                      adv2500 = MDIO_AN_10GBT_CTRL_ADV2_5G;
++                      adv |= MDIO_AN_10GBT_CTRL_ADV2_5G;
++              if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
++                                    phydev->advertising))
++                      adv |= MDIO_AN_10GBT_CTRL_ADV5G;
+               ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
+-                                             MDIO_AN_10GBT_CTRL_ADV2_5G,
+-                                             adv2500);
++                                             MDIO_AN_10GBT_CTRL_ADV2_5G |
++                                             MDIO_AN_10GBT_CTRL_ADV5G,
++                                             adv);
+               if (ret < 0)
+                       return ret;
+       }
diff --git a/target/linux/generic/backport-6.6/781-04-v6.9-net-phy-realtek-use-generic-MDIO-helpers-to-simplify.patch b/target/linux/generic/backport-6.6/781-04-v6.9-net-phy-realtek-use-generic-MDIO-helpers-to-simplify.patch
new file mode 100644 (file)
index 0000000..86db2df
--- /dev/null
@@ -0,0 +1,52 @@
+From b63cc73341e076961d564a74cc3d29b2fd444079 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Thu, 8 Feb 2024 07:59:18 +0100
+Subject: [PATCH] net: phy: realtek: use generic MDIO helpers to simplify the
+ code
+
+Use generic MDIO helpers to simplify the code.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/422ae70f-7305-45fd-ab3e-0dd604b9fd6c@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/realtek.c | 20 +++-----------------
+ 1 file changed, 3 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -682,14 +682,7 @@ static int rtl822x_config_aneg(struct ph
+       int ret = 0;
+       if (phydev->autoneg == AUTONEG_ENABLE) {
+-              u16 adv = 0;
+-
+-              if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+-                                    phydev->advertising))
+-                      adv |= MDIO_AN_10GBT_CTRL_ADV2_5G;
+-              if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+-                                    phydev->advertising))
+-                      adv |= MDIO_AN_10GBT_CTRL_ADV5G;
++              u16 adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising);
+               ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
+                                              MDIO_AN_10GBT_CTRL_ADV2_5G |
+@@ -712,15 +705,8 @@ static int rtl822x_read_status(struct ph
+               if (lpadv < 0)
+                       return lpadv;
+-              linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+-                               phydev->lp_advertising,
+-                               lpadv & MDIO_AN_10GBT_STAT_LP10G);
+-              linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+-                               phydev->lp_advertising,
+-                               lpadv & MDIO_AN_10GBT_STAT_LP5G);
+-              linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+-                               phydev->lp_advertising,
+-                               lpadv & MDIO_AN_10GBT_STAT_LP2_5G);
++              mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising,
++                                                lpadv);
+       }
+       ret = genphy_read_status(phydev);
diff --git a/target/linux/generic/backport-6.6/781-05-v6.10-net-phy-realtek-configure-SerDes-mode-for-rtl822xb-P.patch b/target/linux/generic/backport-6.6/781-05-v6.10-net-phy-realtek-configure-SerDes-mode-for-rtl822xb-P.patch
new file mode 100644 (file)
index 0000000..ba24ca3
--- /dev/null
@@ -0,0 +1,209 @@
+From deb8af5243504e379878ae3f9a091b21422d65b2 Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Tue, 9 Apr 2024 09:30:11 +0200
+Subject: [PATCH] net: phy: realtek: configure SerDes mode for rtl822xb PHYs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The rtl8221b and rtl8226b series support switching SerDes mode between
+2500base-x and sgmii based on the negotiated copper speed.
+
+Configure this switching mode according to SerDes modes supported by
+host.
+
+There is an additional datasheet for RTL8226B/RTL8221B called
+"SERDES MODE SETTING FLOW APPLICATION NOTE" where a sequence is
+described to setup interface and rate adapter mode.
+
+However, there is no documentation about the meaning of registers
+and bits, it's literally just magic numbers and pseudo-code.
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+[ refactored, dropped HiSGMII mode and changed commit message ]
+Signed-off-by: Marek Behún <kabel@kernel.org>
+[ changed rtl822x_update_interface() to use vendor register ]
+[ always fill in possible interfaces ]
+[ only apply to rtl8221b and rtl8226b phy's ]
+[ set phydev->rate_matching in .config_init() ]
+Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: should come before them, without any blank lines. As the
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/realtek.c | 114 ++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 110 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -54,6 +54,16 @@
+                                                RTL8201F_ISR_LINK)
+ #define RTL8201F_IER                          0x13
++#define RTL822X_VND1_SERDES_OPTION                    0x697a
++#define RTL822X_VND1_SERDES_OPTION_MODE_MASK          GENMASK(5, 0)
++#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII               0
++#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX             2
++
++#define RTL822X_VND1_SERDES_CTRL3                     0x7580
++#define RTL822X_VND1_SERDES_CTRL3_MODE_MASK           GENMASK(5, 0)
++#define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII                  0x02
++#define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX              0x16
++
+ #define RTL8366RB_POWER_SAVE                  0x15
+ #define RTL8366RB_POWER_SAVE_ON                       BIT(12)
+@@ -659,6 +669,63 @@ static int rtl822x_write_mmd(struct phy_
+       return ret;
+ }
++static int rtl822xb_config_init(struct phy_device *phydev)
++{
++      bool has_2500, has_sgmii;
++      u16 mode;
++      int ret;
++
++      has_2500 = test_bit(PHY_INTERFACE_MODE_2500BASEX,
++                          phydev->host_interfaces) ||
++                 phydev->interface == PHY_INTERFACE_MODE_2500BASEX;
++
++      has_sgmii = test_bit(PHY_INTERFACE_MODE_SGMII,
++                           phydev->host_interfaces) ||
++                  phydev->interface == PHY_INTERFACE_MODE_SGMII;
++
++      /* fill in possible interfaces */
++      __assign_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->possible_interfaces,
++                   has_2500);
++      __assign_bit(PHY_INTERFACE_MODE_SGMII, phydev->possible_interfaces,
++                   has_sgmii);
++
++      if (!has_2500 && !has_sgmii)
++              return 0;
++
++      /* determine SerDes option mode */
++      if (has_2500 && !has_sgmii) {
++              mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX;
++              phydev->rate_matching = RATE_MATCH_PAUSE;
++      } else {
++              mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII;
++              phydev->rate_matching = RATE_MATCH_NONE;
++      }
++
++      /* the following sequence with magic numbers sets up the SerDes
++       * option mode
++       */
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1,
++                                   RTL822X_VND1_SERDES_OPTION,
++                                   RTL822X_VND1_SERDES_OPTION_MODE_MASK,
++                                   mode);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f10, 0xd455);
++      if (ret < 0)
++              return ret;
++
++      return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
++}
++
+ static int rtl822x_get_features(struct phy_device *phydev)
+ {
+       int val;
+@@ -695,6 +762,28 @@ static int rtl822x_config_aneg(struct ph
+       return __genphy_config_aneg(phydev, ret);
+ }
++static void rtl822xb_update_interface(struct phy_device *phydev)
++{
++      int val;
++
++      if (!phydev->link)
++              return;
++
++      /* Change interface according to serdes mode */
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CTRL3);
++      if (val < 0)
++              return;
++
++      switch (val & RTL822X_VND1_SERDES_CTRL3_MODE_MASK) {
++      case RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX:
++              phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
++              break;
++      case RTL822X_VND1_SERDES_CTRL3_MODE_SGMII:
++              phydev->interface = PHY_INTERFACE_MODE_SGMII;
++              break;
++      }
++}
++
+ static int rtl822x_read_status(struct phy_device *phydev)
+ {
+       int ret;
+@@ -716,6 +805,19 @@ static int rtl822x_read_status(struct ph
+       return rtlgen_get_speed(phydev);
+ }
++static int rtl822xb_read_status(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = rtl822x_read_status(phydev);
++      if (ret < 0)
++              return ret;
++
++      rtl822xb_update_interface(phydev);
++
++      return 0;
++}
++
+ static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
+ {
+       int val;
+@@ -988,7 +1090,8 @@ static struct phy_driver realtek_drvs[]
+               .name           = "RTL8226B_RTL8221B 2.5Gbps PHY",
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+-              .read_status    = rtl822x_read_status,
++              .config_init    = rtl822xb_config_init,
++              .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+               .resume         = rtlgen_resume,
+               .read_page      = rtl821x_read_page,
+@@ -1010,7 +1113,8 @@ static struct phy_driver realtek_drvs[]
+               .name           = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+-              .read_status    = rtl822x_read_status,
++              .config_init    = rtl822xb_config_init,
++              .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+               .resume         = rtlgen_resume,
+               .read_page      = rtl821x_read_page,
+@@ -1020,7 +1124,8 @@ static struct phy_driver realtek_drvs[]
+               .name           = "RTL8221B-VB-CG 2.5Gbps PHY",
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+-              .read_status    = rtl822x_read_status,
++              .config_init    = rtl822xb_config_init,
++              .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+               .resume         = rtlgen_resume,
+               .read_page      = rtl821x_read_page,
+@@ -1030,7 +1135,8 @@ static struct phy_driver realtek_drvs[]
+               .name           = "RTL8221B-VM-CG 2.5Gbps PHY",
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+-              .read_status    = rtl822x_read_status,
++              .config_init    = rtl822xb_config_init,
++              .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+               .resume         = rtlgen_resume,
+               .read_page      = rtl821x_read_page,
diff --git a/target/linux/generic/backport-6.6/781-06-v6.10-net-phy-realtek-add-get_rate_matching-for-rtl822xb-P.patch b/target/linux/generic/backport-6.6/781-06-v6.10-net-phy-realtek-add-get_rate_matching-for-rtl822xb-P.patch
new file mode 100644 (file)
index 0000000..609ae1a
--- /dev/null
@@ -0,0 +1,77 @@
+From c189dbd738243be6775bb6878366bf63e27bfd05 Mon Sep 17 00:00:00 2001
+From: Eric Woudstra <ericwouds@gmail.com>
+Date: Tue, 9 Apr 2024 09:30:12 +0200
+Subject: [PATCH] net: phy: realtek: add get_rate_matching() for rtl822xb PHYs
+
+Uses vendor register to determine if SerDes is setup in rate-matching mode.
+
+Rate-matching only supported when SerDes is set to 2500base-x.
+
+Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/realtek.c | 25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -726,6 +726,27 @@ static int rtl822xb_config_init(struct p
+       return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
+ }
++static int rtl822xb_get_rate_matching(struct phy_device *phydev,
++                                    phy_interface_t iface)
++{
++      int val;
++
++      /* Only rate matching at 2500base-x */
++      if (iface != PHY_INTERFACE_MODE_2500BASEX)
++              return RATE_MATCH_NONE;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_OPTION);
++      if (val < 0)
++              return val;
++
++      if ((val & RTL822X_VND1_SERDES_OPTION_MODE_MASK) ==
++          RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX)
++              return RATE_MATCH_PAUSE;
++
++      /* RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII */
++      return RATE_MATCH_NONE;
++}
++
+ static int rtl822x_get_features(struct phy_device *phydev)
+ {
+       int val;
+@@ -1091,6 +1112,7 @@ static struct phy_driver realtek_drvs[]
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
++              .get_rate_matching = rtl822xb_get_rate_matching,
+               .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+               .resume         = rtlgen_resume,
+@@ -1114,6 +1136,7 @@ static struct phy_driver realtek_drvs[]
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
++              .get_rate_matching = rtl822xb_get_rate_matching,
+               .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+               .resume         = rtlgen_resume,
+@@ -1125,6 +1148,7 @@ static struct phy_driver realtek_drvs[]
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
++              .get_rate_matching = rtl822xb_get_rate_matching,
+               .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+               .resume         = rtlgen_resume,
+@@ -1136,6 +1160,7 @@ static struct phy_driver realtek_drvs[]
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
++              .get_rate_matching = rtl822xb_get_rate_matching,
+               .read_status    = rtl822xb_read_status,
+               .suspend        = genphy_suspend,
+               .resume         = rtlgen_resume,
diff --git a/target/linux/generic/backport-6.6/781-07-v6.10-net-phy-realtek-Add-driver-instances-for-rtl8221b-vi.patch b/target/linux/generic/backport-6.6/781-07-v6.10-net-phy-realtek-Add-driver-instances-for-rtl8221b-vi.patch
new file mode 100644 (file)
index 0000000..5366c72
--- /dev/null
@@ -0,0 +1,218 @@
+From ad5ce743a6b0329f642d80be50ef7b534e908fba Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Tue, 9 Apr 2024 09:30:13 +0200
+Subject: [PATCH] net: phy: realtek: Add driver instances for rtl8221b via
+ Clause 45
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Collected from several commits in [PATCH net-next]
+"Realtek RTL822x PHY rework to c45 and SerDes interface switching"
+
+The instances are used by Clause 45 only accessible PHY's on several sfp
+modules, which are using RollBall protocol.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+[ Added matching functions to differentiate C45 instances ]
+Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/realtek.c | 135 ++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 131 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -64,6 +64,13 @@
+ #define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII                  0x02
+ #define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX              0x16
++/* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45
++ * is set, they cannot be accessed by C45-over-C22.
++ */
++#define RTL822X_VND2_GBCR                             0xa412
++
++#define RTL822X_VND2_GANLPAR                          0xa414
++
+ #define RTL8366RB_POWER_SAVE                  0x15
+ #define RTL8366RB_POWER_SAVE_ON                       BIT(12)
+@@ -74,6 +81,9 @@
+ #define RTL_GENERIC_PHYID                     0x001cc800
+ #define RTL_8211FVD_PHYID                     0x001cc878
++#define RTL_8221B_VB_CG                               0x001cc849
++#define RTL_8221B_VN_CG                               0x001cc84a
++#define RTL_8251B                             0x001cc862
+ MODULE_DESCRIPTION("Realtek PHY driver");
+ MODULE_AUTHOR("Johnson Leung");
+@@ -839,6 +849,67 @@ static int rtl822xb_read_status(struct p
+       return 0;
+ }
++static int rtl822x_c45_config_aneg(struct phy_device *phydev)
++{
++      bool changed = false;
++      int ret, val;
++
++      if (phydev->autoneg == AUTONEG_DISABLE)
++              return genphy_c45_pma_setup_forced(phydev);
++
++      ret = genphy_c45_an_config_aneg(phydev);
++      if (ret < 0)
++              return ret;
++      if (ret > 0)
++              changed = true;
++
++      val = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
++
++      /* Vendor register as C45 has no standardized support for 1000BaseT */
++      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL822X_VND2_GBCR,
++                                   ADVERTISE_1000FULL, val);
++      if (ret < 0)
++              return ret;
++      if (ret > 0)
++              changed = true;
++
++      return genphy_c45_check_and_restart_aneg(phydev, changed);
++}
++
++static int rtl822x_c45_read_status(struct phy_device *phydev)
++{
++      int ret, val;
++
++      ret = genphy_c45_read_status(phydev);
++      if (ret < 0)
++              return ret;
++
++      /* Vendor register as C45 has no standardized support for 1000BaseT */
++      if (phydev->autoneg == AUTONEG_ENABLE) {
++              val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
++                                 RTL822X_VND2_GANLPAR);
++              if (val < 0)
++                      return val;
++
++              mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
++      }
++
++      return 0;
++}
++
++static int rtl822xb_c45_read_status(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = rtl822x_c45_read_status(phydev);
++      if (ret < 0)
++              return ret;
++
++      rtl822xb_update_interface(phydev);
++
++      return 0;
++}
++
+ static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
+ {
+       int val;
+@@ -862,6 +933,35 @@ static int rtl8226_match_phy_device(stru
+              rtlgen_supports_2_5gbps(phydev);
+ }
++static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
++                             bool is_c45)
++{
++      if (phydev->is_c45)
++              return is_c45 && (id == phydev->c45_ids.device_ids[1]);
++      else
++              return !is_c45 && (id == phydev->phy_id);
++}
++
++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
++{
++      return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false);
++}
++
++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev)
++{
++      return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
++}
++
++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev)
++{
++      return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
++}
++
++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev)
++{
++      return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
++}
++
+ static int rtlgen_resume(struct phy_device *phydev)
+ {
+       int ret = genphy_resume(phydev);
+@@ -872,6 +972,15 @@ static int rtlgen_resume(struct phy_devi
+       return ret;
+ }
++static int rtlgen_c45_resume(struct phy_device *phydev)
++{
++      int ret = genphy_c45_pma_resume(phydev);
++
++      msleep(20);
++
++      return ret;
++}
++
+ static int rtl9000a_config_init(struct phy_device *phydev)
+ {
+       phydev->autoneg = AUTONEG_DISABLE;
+@@ -1143,8 +1252,8 @@ static struct phy_driver realtek_drvs[]
+               .read_page      = rtl821x_read_page,
+               .write_page     = rtl821x_write_page,
+       }, {
+-              PHY_ID_MATCH_EXACT(0x001cc849),
+-              .name           = "RTL8221B-VB-CG 2.5Gbps PHY",
++              .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
++              .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
+@@ -1155,8 +1264,17 @@ static struct phy_driver realtek_drvs[]
+               .read_page      = rtl821x_read_page,
+               .write_page     = rtl821x_write_page,
+       }, {
+-              PHY_ID_MATCH_EXACT(0x001cc84a),
+-              .name           = "RTL8221B-VM-CG 2.5Gbps PHY",
++              .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
++              .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
++              .config_init    = rtl822xb_config_init,
++              .get_rate_matching = rtl822xb_get_rate_matching,
++              .config_aneg    = rtl822x_c45_config_aneg,
++              .read_status    = rtl822xb_c45_read_status,
++              .suspend        = genphy_c45_pma_suspend,
++              .resume         = rtlgen_c45_resume,
++      }, {
++              .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
++              .name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
+@@ -1167,6 +1285,15 @@ static struct phy_driver realtek_drvs[]
+               .read_page      = rtl821x_read_page,
+               .write_page     = rtl821x_write_page,
+       }, {
++              .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
++              .name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
++              .config_init    = rtl822xb_config_init,
++              .get_rate_matching = rtl822xb_get_rate_matching,
++              .config_aneg    = rtl822x_c45_config_aneg,
++              .read_status    = rtl822xb_c45_read_status,
++              .suspend        = genphy_c45_pma_suspend,
++              .resume         = rtlgen_c45_resume,
++      }, {
+               PHY_ID_MATCH_EXACT(0x001cc862),
+               .name           = "RTL8251B 5Gbps PHY",
+               .get_features   = rtl822x_get_features,
diff --git a/target/linux/generic/backport-6.6/781-08-v6.10-net-phy-realtek-Change-rtlgen_get_speed-to-rtlgen_de.patch b/target/linux/generic/backport-6.6/781-08-v6.10-net-phy-realtek-Change-rtlgen_get_speed-to-rtlgen_de.patch
new file mode 100644 (file)
index 0000000..5c7130d
--- /dev/null
@@ -0,0 +1,125 @@
+From 2e4ea707c7e04eb83e58c43e0e744bbdf6b23ff2 Mon Sep 17 00:00:00 2001
+From: Eric Woudstra <ericwouds@gmail.com>
+Date: Tue, 9 Apr 2024 09:30:14 +0200
+Subject: [PATCH] net: phy: realtek: Change rtlgen_get_speed() to
+ rtlgen_decode_speed()
+
+The value of the register to determine the speed, is retrieved
+differently when using Clause 45 only. To use the rtlgen_get_speed()
+function in this case, pass the value of the register as argument to
+rtlgen_get_speed(). The function would then always return 0, so change it
+to void. A better name for this function now is rtlgen_decode_speed().
+
+Replace a call to genphy_read_status() followed by rtlgen_get_speed()
+with a call to rtlgen_read_status() in rtl822x_read_status().
+
+Add reading speed to rtl822x_c45_read_status().
+
+Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/realtek.c | 46 +++++++++++++++++++++------------------
+ 1 file changed, 25 insertions(+), 21 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -71,6 +71,8 @@
+ #define RTL822X_VND2_GANLPAR                          0xa414
++#define RTL822X_VND2_PHYSR                            0xa434
++
+ #define RTL8366RB_POWER_SAVE                  0x15
+ #define RTL8366RB_POWER_SAVE_ON                       BIT(12)
+@@ -551,17 +553,8 @@ static int rtl8366rb_config_init(struct
+ }
+ /* get actual speed to cover the downshift case */
+-static int rtlgen_get_speed(struct phy_device *phydev)
++static void rtlgen_decode_speed(struct phy_device *phydev, int val)
+ {
+-      int val;
+-
+-      if (!phydev->link)
+-              return 0;
+-
+-      val = phy_read_paged(phydev, 0xa43, 0x12);
+-      if (val < 0)
+-              return val;
+-
+       switch (val & RTLGEN_SPEED_MASK) {
+       case 0x0000:
+               phydev->speed = SPEED_10;
+@@ -584,19 +577,26 @@ static int rtlgen_get_speed(struct phy_d
+       default:
+               break;
+       }
+-
+-      return 0;
+ }
+ static int rtlgen_read_status(struct phy_device *phydev)
+ {
+-      int ret;
++      int ret, val;
+       ret = genphy_read_status(phydev);
+       if (ret < 0)
+               return ret;
+-      return rtlgen_get_speed(phydev);
++      if (!phydev->link)
++              return 0;
++
++      val = phy_read_paged(phydev, 0xa43, 0x12);
++      if (val < 0)
++              return val;
++
++      rtlgen_decode_speed(phydev, val);
++
++      return 0;
+ }
+ static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
+@@ -817,8 +817,6 @@ static void rtl822xb_update_interface(st
+ static int rtl822x_read_status(struct phy_device *phydev)
+ {
+-      int ret;
+-
+       if (phydev->autoneg == AUTONEG_ENABLE) {
+               int lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
+@@ -829,11 +827,7 @@ static int rtl822x_read_status(struct ph
+                                                 lpadv);
+       }
+-      ret = genphy_read_status(phydev);
+-      if (ret < 0)
+-              return ret;
+-
+-      return rtlgen_get_speed(phydev);
++      return rtlgen_read_status(phydev);
+ }
+ static int rtl822xb_read_status(struct phy_device *phydev)
+@@ -894,6 +888,16 @@ static int rtl822x_c45_read_status(struc
+               mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
+       }
++      if (!phydev->link)
++              return 0;
++
++      /* Read actual speed from vendor register. */
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_PHYSR);
++      if (val < 0)
++              return val;
++
++      rtlgen_decode_speed(phydev, val);
++
+       return 0;
+ }
diff --git a/target/linux/generic/backport-6.6/781-09-v6.10-net-phy-realtek-add-rtl822x_c45_get_features-to-set-.patch b/target/linux/generic/backport-6.6/781-09-v6.10-net-phy-realtek-add-rtl822x_c45_get_features-to-set-.patch
new file mode 100644 (file)
index 0000000..ed29dcd
--- /dev/null
@@ -0,0 +1,48 @@
+From 2d9ce64862705b33397d54dafecc5f51d8b1bb06 Mon Sep 17 00:00:00 2001
+From: Eric Woudstra <ericwouds@gmail.com>
+Date: Tue, 9 Apr 2024 09:30:15 +0200
+Subject: [PATCH] net: phy: realtek: add rtl822x_c45_get_features() to set
+ supported port
+
+Sets ETHTOOL_LINK_MODE_TP_BIT in phydev->supported.
+
+Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/realtek.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -843,6 +843,14 @@ static int rtl822xb_read_status(struct p
+       return 0;
+ }
++static int rtl822x_c45_get_features(struct phy_device *phydev)
++{
++      linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT,
++                       phydev->supported);
++
++      return genphy_c45_pma_read_abilities(phydev);
++}
++
+ static int rtl822x_c45_config_aneg(struct phy_device *phydev)
+ {
+       bool changed = false;
+@@ -1272,6 +1280,7 @@ static struct phy_driver realtek_drvs[]
+               .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
+               .config_init    = rtl822xb_config_init,
+               .get_rate_matching = rtl822xb_get_rate_matching,
++              .get_features   = rtl822x_c45_get_features,
+               .config_aneg    = rtl822x_c45_config_aneg,
+               .read_status    = rtl822xb_c45_read_status,
+               .suspend        = genphy_c45_pma_suspend,
+@@ -1293,6 +1302,7 @@ static struct phy_driver realtek_drvs[]
+               .name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
+               .config_init    = rtl822xb_config_init,
+               .get_rate_matching = rtl822xb_get_rate_matching,
++              .get_features   = rtl822x_c45_get_features,
+               .config_aneg    = rtl822x_c45_config_aneg,
+               .read_status    = rtl822xb_c45_read_status,
+               .suspend        = genphy_c45_pma_suspend,
diff --git a/target/linux/generic/backport-6.6/781-10-v6.11-net-phy-realtek-add-support-for-rtl8224-2.5Gbps-PHY.patch b/target/linux/generic/backport-6.6/781-10-v6.11-net-phy-realtek-add-support-for-rtl8224-2.5Gbps-PHY.patch
new file mode 100644 (file)
index 0000000..13e1d88
--- /dev/null
@@ -0,0 +1,33 @@
+From 9e42a2ea7f6703e2092c39171c2bf1fd7eec0bd3 Mon Sep 17 00:00:00 2001
+From: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Date: Tue, 11 Jun 2024 17:34:14 +1200
+Subject: [PATCH] net: phy: realtek: add support for rtl8224 2.5Gbps PHY
+
+The Realtek RTL8224 PHY is a 2.5Gbps capable PHY. It only uses the
+clause 45 MDIO interface and can leverage the support that has already
+been added for the other 822x PHYs.
+
+Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Link: https://lore.kernel.org/r/20240611053415.2111723-1-chris.packham@alliedtelesis.co.nz
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/realtek.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1318,6 +1318,14 @@ static struct phy_driver realtek_drvs[]
+               .read_page      = rtl821x_read_page,
+               .write_page     = rtl821x_write_page,
+       }, {
++              PHY_ID_MATCH_EXACT(0x001ccad0),
++              .name           = "RTL8224 2.5Gbps PHY",
++              .get_features   = rtl822x_c45_get_features,
++              .config_aneg    = rtl822x_c45_config_aneg,
++              .read_status    = rtl822x_c45_read_status,
++              .suspend        = genphy_c45_pma_suspend,
++              .resume         = rtlgen_c45_resume,
++      }, {
+               PHY_ID_MATCH_EXACT(0x001cc961),
+               .name           = "RTL8366RB Gigabit Ethernet",
+               .config_init    = &rtl8366rb_config_init,
diff --git a/target/linux/generic/backport-6.6/781-11-v6.11-net-phy-realtek-Add-support-for-PHY-LEDs-on-RTL8211F.patch b/target/linux/generic/backport-6.6/781-11-v6.11-net-phy-realtek-Add-support-for-PHY-LEDs-on-RTL8211F.patch
new file mode 100644 (file)
index 0000000..12c8ae6
--- /dev/null
@@ -0,0 +1,151 @@
+From 17784801d888238571a0c4101b9ac4401fffeaa0 Mon Sep 17 00:00:00 2001
+From: Marek Vasut <marex@denx.de>
+Date: Tue, 25 Jun 2024 22:42:17 +0200
+Subject: [PATCH] net: phy: realtek: Add support for PHY LEDs on RTL8211F
+
+Realtek RTL8211F Ethernet PHY supports 3 LED pins which are used to
+indicate link status and activity. Add minimal LED controller driver
+supporting the most common uses with the 'netdev' trigger.
+
+Signed-off-by: Marek Vasut <marex@denx.de>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/realtek.c | 106 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 106 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -32,6 +32,15 @@
+ #define RTL8211F_PHYCR2                               0x19
+ #define RTL8211F_INSR                         0x1d
++#define RTL8211F_LEDCR                                0x10
++#define RTL8211F_LEDCR_MODE                   BIT(15)
++#define RTL8211F_LEDCR_ACT_TXRX                       BIT(4)
++#define RTL8211F_LEDCR_LINK_1000              BIT(3)
++#define RTL8211F_LEDCR_LINK_100                       BIT(1)
++#define RTL8211F_LEDCR_LINK_10                        BIT(0)
++#define RTL8211F_LEDCR_MASK                   GENMASK(4, 0)
++#define RTL8211F_LEDCR_SHIFT                  5
++
+ #define RTL8211F_TX_DELAY                     BIT(8)
+ #define RTL8211F_RX_DELAY                     BIT(3)
+@@ -87,6 +96,8 @@
+ #define RTL_8221B_VN_CG                               0x001cc84a
+ #define RTL_8251B                             0x001cc862
++#define RTL8211F_LED_COUNT                    3
++
+ MODULE_DESCRIPTION("Realtek PHY driver");
+ MODULE_AUTHOR("Johnson Leung");
+ MODULE_LICENSE("GPL");
+@@ -476,6 +487,98 @@ static int rtl821x_resume(struct phy_dev
+       return 0;
+ }
++static int rtl8211f_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                      unsigned long rules)
++{
++      const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) |
++                                 BIT(TRIGGER_NETDEV_LINK_100) |
++                                 BIT(TRIGGER_NETDEV_LINK_1000) |
++                                 BIT(TRIGGER_NETDEV_RX) |
++                                 BIT(TRIGGER_NETDEV_TX);
++
++      /* The RTL8211F PHY supports these LED settings on up to three LEDs:
++       * - Link: Configurable subset of 10/100/1000 link rates
++       * - Active: Blink on activity, RX or TX is not differentiated
++       * The Active option has two modes, A and B:
++       * - A: Link and Active indication at configurable, but matching,
++       *      subset of 10/100/1000 link rates
++       * - B: Link indication at configurable subset of 10/100/1000 link
++       *      rates and Active indication always at all three 10+100+1000
++       *      link rates.
++       * This code currently uses mode B only.
++       */
++
++      if (index >= RTL8211F_LED_COUNT)
++              return -EINVAL;
++
++      /* Filter out any other unsupported triggers. */
++      if (rules & ~mask)
++              return -EOPNOTSUPP;
++
++      /* RX and TX are not differentiated, either both are set or not set. */
++      if (!(rules & BIT(TRIGGER_NETDEV_RX)) ^ !(rules & BIT(TRIGGER_NETDEV_TX)))
++              return -EOPNOTSUPP;
++
++      return 0;
++}
++
++static int rtl8211f_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                     unsigned long *rules)
++{
++      int val;
++
++      val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR);
++      if (val < 0)
++              return val;
++
++      val >>= RTL8211F_LEDCR_SHIFT * index;
++      val &= RTL8211F_LEDCR_MASK;
++
++      if (val & RTL8211F_LEDCR_LINK_10)
++              set_bit(TRIGGER_NETDEV_LINK_10, rules);
++
++      if (val & RTL8211F_LEDCR_LINK_100)
++              set_bit(TRIGGER_NETDEV_LINK_100, rules);
++
++      if (val & RTL8211F_LEDCR_LINK_1000)
++              set_bit(TRIGGER_NETDEV_LINK_1000, rules);
++
++      if (val & RTL8211F_LEDCR_ACT_TXRX) {
++              set_bit(TRIGGER_NETDEV_RX, rules);
++              set_bit(TRIGGER_NETDEV_TX, rules);
++      }
++
++      return 0;
++}
++
++static int rtl8211f_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                     unsigned long rules)
++{
++      const u16 mask = RTL8211F_LEDCR_MASK << (RTL8211F_LEDCR_SHIFT * index);
++      u16 reg = RTL8211F_LEDCR_MODE;  /* Mode B */
++
++      if (index >= RTL8211F_LED_COUNT)
++              return -EINVAL;
++
++      if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
++              reg |= RTL8211F_LEDCR_LINK_10;
++
++      if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++              reg |= RTL8211F_LEDCR_LINK_100;
++
++      if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++              reg |= RTL8211F_LEDCR_LINK_1000;
++
++      if (test_bit(TRIGGER_NETDEV_RX, &rules) ||
++          test_bit(TRIGGER_NETDEV_TX, &rules)) {
++              reg |= RTL8211F_LEDCR_ACT_TXRX;
++      }
++
++      reg <<= RTL8211F_LEDCR_SHIFT * index;
++
++      return phy_modify_paged(phydev, 0xd04, RTL8211F_LEDCR, mask, reg);
++}
++
+ static int rtl8211e_config_init(struct phy_device *phydev)
+ {
+       int ret = 0, oldpage;
+@@ -1192,6 +1295,9 @@ static struct phy_driver realtek_drvs[]
+               .read_page      = rtl821x_read_page,
+               .write_page     = rtl821x_write_page,
+               .flags          = PHY_ALWAYS_CALL_SUSPEND,
++              .led_hw_is_supported = rtl8211f_led_hw_is_supported,
++              .led_hw_control_get = rtl8211f_led_hw_control_get,
++              .led_hw_control_set = rtl8211f_led_hw_control_set,
+       }, {
+               PHY_ID_MATCH_EXACT(RTL_8211FVD_PHYID),
+               .name           = "RTL8211F-VD Gigabit Ethernet",
diff --git a/target/linux/generic/backport-6.6/781-12-v6.11-net-phy-realtek-Fix-setting-of-PHY-LEDs-Mode-B-bit-o.patch b/target/linux/generic/backport-6.6/781-12-v6.11-net-phy-realtek-Fix-setting-of-PHY-LEDs-Mode-B-bit-o.patch
new file mode 100644 (file)
index 0000000..5958fc9
--- /dev/null
@@ -0,0 +1,42 @@
+From a2f5c505b4378cd6fc7c4a44ff3665ccef2037db Mon Sep 17 00:00:00 2001
+From: Sava Jakovljev <savaj@meyersound.com>
+Date: Wed, 21 Aug 2024 04:16:57 +0200
+Subject: [PATCH] net: phy: realtek: Fix setting of PHY LEDs Mode B bit on
+ RTL8211F
+
+The current implementation incorrectly sets the mode bit of the PHY chip.
+Bit 15 (RTL8211F_LEDCR_MODE) should not be shifted together with the
+configuration nibble of a LED- it should be set independently of the
+index of the LED being configured.
+As a consequence, the RTL8211F LED control is actually operating in Mode A.
+Fix the error by or-ing final register value to write with a const-value of
+RTL8211F_LEDCR_MODE, thus setting Mode bit explicitly.
+
+Fixes: 17784801d888 ("net: phy: realtek: Add support for PHY LEDs on RTL8211F")
+Signed-off-by: Sava Jakovljev <savaj@meyersound.com>
+Reviewed-by: Marek Vasut <marex@denx.de>
+Link: https://patch.msgid.link/PAWP192MB21287372F30C4E55B6DF6158C38E2@PAWP192MB2128.EURP192.PROD.OUTLOOK.COM
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/realtek.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -555,7 +555,7 @@ static int rtl8211f_led_hw_control_set(s
+                                      unsigned long rules)
+ {
+       const u16 mask = RTL8211F_LEDCR_MASK << (RTL8211F_LEDCR_SHIFT * index);
+-      u16 reg = RTL8211F_LEDCR_MODE;  /* Mode B */
++      u16 reg = 0;
+       if (index >= RTL8211F_LED_COUNT)
+               return -EINVAL;
+@@ -575,6 +575,7 @@ static int rtl8211f_led_hw_control_set(s
+       }
+       reg <<= RTL8211F_LEDCR_SHIFT * index;
++      reg |= RTL8211F_LEDCR_MODE;      /* Mode B */
+       return phy_modify_paged(phydev, 0xd04, RTL8211F_LEDCR, mask, reg);
+ }
diff --git a/target/linux/generic/backport-6.6/781-13-v6.12-net-phy-realtek-Check-the-index-value-in-led_hw_cont.patch b/target/linux/generic/backport-6.6/781-13-v6.12-net-phy-realtek-Check-the-index-value-in-led_hw_cont.patch
new file mode 100644 (file)
index 0000000..a6f8b6d
--- /dev/null
@@ -0,0 +1,32 @@
+From c283782fc5d60c4d8169137c6f955aa3553d3b3d Mon Sep 17 00:00:00 2001
+From: Hui Wang <hui.wang@canonical.com>
+Date: Fri, 27 Sep 2024 19:46:10 +0800
+Subject: [PATCH] net: phy: realtek: Check the index value in
+ led_hw_control_get
+
+Just like rtl8211f_led_hw_is_supported() and
+rtl8211f_led_hw_control_set(), the rtl8211f_led_hw_control_get() also
+needs to check the index value, otherwise the caller is likely to get
+an incorrect rules.
+
+Fixes: 17784801d888 ("net: phy: realtek: Add support for PHY LEDs on RTL8211F")
+Signed-off-by: Hui Wang <hui.wang@canonical.com>
+Reviewed-by: Marek Vasut <marex@denx.de>
+Link: https://patch.msgid.link/20240927114610.1278935-1-hui.wang@canonical.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/realtek.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -527,6 +527,9 @@ static int rtl8211f_led_hw_control_get(s
+ {
+       int val;
++      if (index >= RTL8211F_LED_COUNT)
++              return -EINVAL;
++
+       val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR);
+       if (val < 0)
+               return val;
diff --git a/target/linux/generic/backport-6.6/781-14-v6.12-net-phy-realtek-Fix-MMD-access-on-RTL8126A-integrate.patch b/target/linux/generic/backport-6.6/781-14-v6.12-net-phy-realtek-Fix-MMD-access-on-RTL8126A-integrate.patch
new file mode 100644 (file)
index 0000000..b50e133
--- /dev/null
@@ -0,0 +1,67 @@
+From a6ad589c1d118f9d5b1bc4c6888d42919f830340 Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Mon, 7 Oct 2024 11:57:41 +0200
+Subject: [PATCH] net: phy: realtek: Fix MMD access on RTL8126A-integrated PHY
+
+All MMD reads return 0 for the RTL8126A-integrated PHY. Therefore phylib
+assumes it doesn't support EEE, what results in higher power consumption,
+and a significantly higher chip temperature in my case.
+To fix this split out the PHY driver for the RTL8126A-integrated PHY
+and set the read_mmd/write_mmd callbacks to read from vendor-specific
+registers.
+
+Fixes: 5befa3728b85 ("net: phy: realtek: add support for RTL8126A-integrated 5Gbps PHY")
+Cc: stable@vger.kernel.org
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/realtek.c | 24 +++++++++++++++++++++++-
+ 1 file changed, 23 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1081,6 +1081,16 @@ static int rtl8221b_vn_cg_c45_match_phy_
+       return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
+ }
++static int rtl8251b_c22_match_phy_device(struct phy_device *phydev)
++{
++      return rtlgen_is_c45_match(phydev, RTL_8251B, false);
++}
++
++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev)
++{
++      return rtlgen_is_c45_match(phydev, RTL_8251B, true);
++}
++
+ static int rtlgen_resume(struct phy_device *phydev)
+ {
+       int ret = genphy_resume(phydev);
+@@ -1418,7 +1428,7 @@ static struct phy_driver realtek_drvs[]
+               .suspend        = genphy_c45_pma_suspend,
+               .resume         = rtlgen_c45_resume,
+       }, {
+-              PHY_ID_MATCH_EXACT(0x001cc862),
++              .match_phy_device = rtl8251b_c45_match_phy_device,
+               .name           = "RTL8251B 5Gbps PHY",
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+@@ -1428,6 +1438,18 @@ static struct phy_driver realtek_drvs[]
+               .read_page      = rtl821x_read_page,
+               .write_page     = rtl821x_write_page,
+       }, {
++              .match_phy_device = rtl8251b_c22_match_phy_device,
++              .name           = "RTL8126A-internal 5Gbps PHY",
++              .get_features   = rtl822x_get_features,
++              .config_aneg    = rtl822x_config_aneg,
++              .read_status    = rtl822x_read_status,
++              .suspend        = genphy_suspend,
++              .resume         = rtlgen_resume,
++              .read_page      = rtl821x_read_page,
++              .write_page     = rtl821x_write_page,
++              .read_mmd       = rtl822x_read_mmd,
++              .write_mmd      = rtl822x_write_mmd,
++      }, {
+               PHY_ID_MATCH_EXACT(0x001ccad0),
+               .name           = "RTL8224 2.5Gbps PHY",
+               .get_features   = rtl822x_c45_get_features,
diff --git a/target/linux/generic/backport-6.6/781-15-v6.13-net-phy-realtek-read-duplex-and-gbit-master-from-PHY.patch b/target/linux/generic/backport-6.6/781-15-v6.13-net-phy-realtek-read-duplex-and-gbit-master-from-PHY.patch
new file mode 100644 (file)
index 0000000..e15218b
--- /dev/null
@@ -0,0 +1,106 @@
+From 081c9c0265c91b8333165aa6230c20bcbc6f7cbf Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 10 Oct 2024 14:07:16 +0100
+Subject: [PATCH 3/5] net: phy: realtek: read duplex and gbit master from PHYSR
+ register
+
+The PHYSR MMD register is present and defined equally for all RTL82xx
+Ethernet PHYs.
+Read duplex and Gbit master bits from rtlgen_decode_speed() and rename
+it to rtlgen_decode_physr().
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/b9a76341da851a18c985bc4774fa295babec79bb.1728565530.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/realtek.c | 41 +++++++++++++++++++++++++++++++--------
+ 1 file changed, 33 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -80,15 +80,18 @@
+ #define RTL822X_VND2_GANLPAR                          0xa414
+-#define RTL822X_VND2_PHYSR                            0xa434
+-
+ #define RTL8366RB_POWER_SAVE                  0x15
+ #define RTL8366RB_POWER_SAVE_ON                       BIT(12)
+ #define RTL9000A_GINMR                                0x14
+ #define RTL9000A_GINMR_LINK_STATUS            BIT(4)
+-#define RTLGEN_SPEED_MASK                     0x0630
++#define RTL_VND2_PHYSR                                0xa434
++#define RTL_VND2_PHYSR_DUPLEX                 BIT(3)
++#define RTL_VND2_PHYSR_SPEEDL                 GENMASK(5, 4)
++#define RTL_VND2_PHYSR_SPEEDH                 GENMASK(10, 9)
++#define RTL_VND2_PHYSR_MASTER                 BIT(11)
++#define RTL_VND2_PHYSR_SPEED_MASK             (RTL_VND2_PHYSR_SPEEDL | RTL_VND2_PHYSR_SPEEDH)
+ #define RTL_GENERIC_PHYID                     0x001cc800
+ #define RTL_8211FVD_PHYID                     0x001cc878
+@@ -660,9 +663,18 @@ static int rtl8366rb_config_init(struct
+ }
+ /* get actual speed to cover the downshift case */
+-static void rtlgen_decode_speed(struct phy_device *phydev, int val)
++static void rtlgen_decode_physr(struct phy_device *phydev, int val)
+ {
+-      switch (val & RTLGEN_SPEED_MASK) {
++      /* bit 3
++       * 0: Half Duplex
++       * 1: Full Duplex
++       */
++      if (val & RTL_VND2_PHYSR_DUPLEX)
++              phydev->duplex = DUPLEX_FULL;
++      else
++              phydev->duplex = DUPLEX_HALF;
++
++      switch (val & RTL_VND2_PHYSR_SPEED_MASK) {
+       case 0x0000:
+               phydev->speed = SPEED_10;
+               break;
+@@ -684,6 +696,19 @@ static void rtlgen_decode_speed(struct p
+       default:
+               break;
+       }
++
++      /* bit 11
++       * 0: Slave Mode
++       * 1: Master Mode
++       */
++      if (phydev->speed >= 1000) {
++              if (val & RTL_VND2_PHYSR_MASTER)
++                      phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
++              else
++                      phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
++      } else {
++              phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
++      }
+ }
+ static int rtlgen_read_status(struct phy_device *phydev)
+@@ -701,7 +726,7 @@ static int rtlgen_read_status(struct phy
+       if (val < 0)
+               return val;
+-      rtlgen_decode_speed(phydev, val);
++      rtlgen_decode_physr(phydev, val);
+       return 0;
+ }
+@@ -1007,11 +1032,11 @@ static int rtl822x_c45_read_status(struc
+               return 0;
+       /* Read actual speed from vendor register. */
+-      val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_PHYSR);
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_VND2_PHYSR);
+       if (val < 0)
+               return val;
+-      rtlgen_decode_speed(phydev, val);
++      rtlgen_decode_physr(phydev, val);
+       return 0;
+ }
diff --git a/target/linux/generic/backport-6.6/781-16-v6.13-net-phy-realtek-change-order-of-calls-in-C22-read_st.patch b/target/linux/generic/backport-6.6/781-16-v6.13-net-phy-realtek-change-order-of-calls-in-C22-read_st.patch
new file mode 100644 (file)
index 0000000..be7136b
--- /dev/null
@@ -0,0 +1,54 @@
+From 68d5cd09e8919679ce13b85950debea4b2e98e04 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 10 Oct 2024 14:07:26 +0100
+Subject: [PATCH 4/5] net: phy: realtek: change order of calls in C22
+ read_status()
+
+Always call rtlgen_read_status() first, so genphy_read_status() which
+is called by it clears bits in case auto-negotiation has not completed.
+Also clear 10GBT link-partner advertisement bits in case auto-negotiation
+is disabled or has not completed.
+
+Suggested-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/b15929a41621d215c6b2b57393368086589569ec.1728565530.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/realtek.c | 22 +++++++++++++++-------
+ 1 file changed, 15 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -949,17 +949,25 @@ static void rtl822xb_update_interface(st
+ static int rtl822x_read_status(struct phy_device *phydev)
+ {
+-      if (phydev->autoneg == AUTONEG_ENABLE) {
+-              int lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
++      int lpadv, ret;
+-              if (lpadv < 0)
+-                      return lpadv;
++      ret = rtlgen_read_status(phydev);
++      if (ret < 0)
++              return ret;
+-              mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising,
+-                                                lpadv);
++      if (phydev->autoneg == AUTONEG_DISABLE ||
++          !phydev->autoneg_complete) {
++              mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
++              return 0;
+       }
+-      return rtlgen_read_status(phydev);
++      lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
++      if (lpadv < 0)
++              return lpadv;
++
++      mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv);
++
++      return 0;
+ }
+ static int rtl822xb_read_status(struct phy_device *phydev)
diff --git a/target/linux/generic/backport-6.6/781-17-v6.13-net-phy-realtek-clear-1000Base-T-link-partner-advert.patch b/target/linux/generic/backport-6.6/781-17-v6.13-net-phy-realtek-clear-1000Base-T-link-partner-advert.patch
new file mode 100644 (file)
index 0000000..3847d58
--- /dev/null
@@ -0,0 +1,30 @@
+From 5cb409b3960e75467cbb0a8e1e5596b4490570e3 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 10 Oct 2024 14:07:39 +0100
+Subject: [PATCH 5/5] net: phy: realtek: clear 1000Base-T link partner
+ advertisement
+
+Clear 1000Base-T link partner advertisement bits in Clause-45
+read_status() function in case auto-negotiation is disabled or has not
+been completed.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://patch.msgid.link/9dc9b47b2d675708afef3ad366bfd78eb584d958.1728565530.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/realtek.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1026,6 +1026,10 @@ static int rtl822x_c45_read_status(struc
+       if (ret < 0)
+               return ret;
++      if (phydev->autoneg == AUTONEG_DISABLE ||
++          !genphy_c45_aneg_done(phydev))
++              mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
++
+       /* Vendor register as C45 has no standardized support for 1000BaseT */
+       if (phydev->autoneg == AUTONEG_ENABLE) {
+               val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
diff --git a/target/linux/generic/backport-6.6/790-01-v6.7-net-dsa-mt7530-Convert-to-platform-remove-callback-r.patch b/target/linux/generic/backport-6.6/790-01-v6.7-net-dsa-mt7530-Convert-to-platform-remove-callback-r.patch
new file mode 100644 (file)
index 0000000..95c6957
--- /dev/null
@@ -0,0 +1,58 @@
+From b91ef50f70e7c092c50c1b92e63ef3fb0041cdd4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
+Date: Mon, 18 Sep 2023 21:19:12 +0200
+Subject: [PATCH 01/30] net: dsa: mt7530: Convert to platform remove callback
+ returning void
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The .remove() callback for a platform driver returns an int which makes
+many driver authors wrongly assume it's possible to do error handling by
+returning an error code. However the value returned is ignored (apart
+from emitting a warning) and this typically results in resource leaks.
+To improve here there is a quest to make the remove callback return
+void. In the first step of this quest all drivers are converted to
+.remove_new() which already returns void. Eventually after all drivers
+are converted, .remove_new() is renamed to .remove().
+
+Trivially convert this driver from always returning zero in the remove
+callback to the void returning variant.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530-mmio.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/dsa/mt7530-mmio.c
++++ b/drivers/net/dsa/mt7530-mmio.c
+@@ -63,15 +63,12 @@ mt7988_probe(struct platform_device *pde
+       return dsa_register_switch(priv->ds);
+ }
+-static int
+-mt7988_remove(struct platform_device *pdev)
++static void mt7988_remove(struct platform_device *pdev)
+ {
+       struct mt7530_priv *priv = platform_get_drvdata(pdev);
+       if (priv)
+               mt7530_remove_common(priv);
+-
+-      return 0;
+ }
+ static void mt7988_shutdown(struct platform_device *pdev)
+@@ -88,7 +85,7 @@ static void mt7988_shutdown(struct platf
+ static struct platform_driver mt7988_platform_driver = {
+       .probe  = mt7988_probe,
+-      .remove = mt7988_remove,
++      .remove_new = mt7988_remove,
+       .shutdown = mt7988_shutdown,
+       .driver = {
+               .name = "mt7530-mmio",
diff --git a/target/linux/generic/backport-6.6/790-02-v6.7-net-dsa-mt753x-remove-mt753x_phylink_pcs_link_up.patch b/target/linux/generic/backport-6.6/790-02-v6.7-net-dsa-mt753x-remove-mt753x_phylink_pcs_link_up.patch
new file mode 100644 (file)
index 0000000..5060243
--- /dev/null
@@ -0,0 +1,51 @@
+From d22c85764665af931c5c61bbe282b4116a88e792 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Wed, 27 Sep 2023 13:13:56 +0100
+Subject: [PATCH 02/30] net: dsa: mt753x: remove mt753x_phylink_pcs_link_up()
+
+Remove the mt753x_phylink_pcs_link_up() function for two reasons:
+
+1) priv->pcs[i].pcs.neg_mode is set true, meaning it doesn't take a
+   MLO_AN_FIXED anymore, but one of PHYLINK_PCS_NEG_*. However, this
+   is inconsequential due to...
+2) priv->pcs[port].pcs.ops is always initialised to point at
+   mt7530_pcs_ops, which does not have a pcs_link_up() member.
+
+So, let's remove mt753x_phylink_pcs_link_up() entirely.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Link: https://lore.kernel.org/r/E1qlTQS-008BWe-Va@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 11 -----------
+ 1 file changed, 11 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -3037,15 +3037,6 @@ static void mt753x_phylink_mac_link_down
+       mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
+ }
+-static void mt753x_phylink_pcs_link_up(struct phylink_pcs *pcs,
+-                                     unsigned int mode,
+-                                     phy_interface_t interface,
+-                                     int speed, int duplex)
+-{
+-      if (pcs->ops->pcs_link_up)
+-              pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
+-}
+-
+ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
+                                      unsigned int mode,
+                                      phy_interface_t interface,
+@@ -3133,8 +3124,6 @@ mt7531_cpu_port_config(struct dsa_switch
+               return ret;
+       mt7530_write(priv, MT7530_PMCR_P(port),
+                    PMCR_CPU_PORT_SETTING(priv->id));
+-      mt753x_phylink_pcs_link_up(&priv->pcs[port].pcs, MLO_AN_FIXED,
+-                                 interface, speed, DUPLEX_FULL);
+       mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL,
+                                  speed, DUPLEX_FULL, true, true);
diff --git a/target/linux/generic/backport-6.6/790-03-v6.7-net-dsa-mt7530-replace-deprecated-strncpy-with-ethto.patch b/target/linux/generic/backport-6.6/790-03-v6.7-net-dsa-mt7530-replace-deprecated-strncpy-with-ethto.patch
new file mode 100644 (file)
index 0000000..2da610e
--- /dev/null
@@ -0,0 +1,40 @@
+From 9b4f1f5a0801652056670a38503b4049eb413caf Mon Sep 17 00:00:00 2001
+From: Justin Stitt <justinstitt@google.com>
+Date: Mon, 9 Oct 2023 18:29:19 +0000
+Subject: [PATCH 03/30] net: dsa: mt7530: replace deprecated strncpy with
+ ethtool_sprintf
+
+`strncpy` is deprecated for use on NUL-terminated destination strings
+[1] and as such we should prefer more robust and less ambiguous string
+interfaces.
+
+ethtool_sprintf() is designed specifically for get_strings() usage.
+Let's replace strncpy in favor of this more robust and easier to
+understand interface.
+
+Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1]
+Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2]
+Link: https://github.com/KSPP/linux/issues/90
+Signed-off-by: Justin Stitt <justinstitt@google.com>
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://lore.kernel.org/r/20231009-strncpy-drivers-net-dsa-mt7530-c-v1-1-ec6677a6436a@google.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -836,8 +836,7 @@ mt7530_get_strings(struct dsa_switch *ds
+               return;
+       for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++)
+-              strncpy(data + i * ETH_GSTRING_LEN, mt7530_mib[i].name,
+-                      ETH_GSTRING_LEN);
++              ethtool_sprintf(&data, "%s", mt7530_mib[i].name);
+ }
+ static void
diff --git a/target/linux/generic/backport-6.6/790-04-v6.9-net-dsa-mt7530-support-OF-based-registration-of-swit.patch b/target/linux/generic/backport-6.6/790-04-v6.9-net-dsa-mt7530-support-OF-based-registration-of-swit.patch
new file mode 100644 (file)
index 0000000..74f6c11
--- /dev/null
@@ -0,0 +1,116 @@
+From af26b0d1bf934bbaa7cafb871a51e95087a088a0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Jan 2024 08:34:31 +0300
+Subject: [PATCH 04/30] net: dsa: mt7530: support OF-based registration of
+ switch MDIO bus
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently the MDIO bus of the switches the MT7530 DSA subdriver controls
+can only be registered as non-OF-based. Bring support for registering the
+bus OF-based.
+
+The subdrivers that control switches [with MDIO bus] probed on OF must
+follow this logic to support all cases properly:
+
+No switch MDIO bus defined: Populate ds->user_mii_bus, register the MDIO
+bus, set the interrupts for PHYs if "interrupt-controller" is defined at
+the switch node. This case should only be covered for the switches which
+their dt-bindings documentation didn't document the MDIO bus from the
+start. This is to keep supporting the device trees that do not describe the
+MDIO bus on the device tree but the MDIO bus is being used nonetheless.
+
+Switch MDIO bus defined: Don't populate ds->user_mii_bus, register the MDIO
+bus, set the interrupts for PHYs if ["interrupt-controller" is defined at
+the switch node and "interrupts" is defined at the PHY nodes under the
+switch MDIO bus node].
+
+Switch MDIO bus defined but explicitly disabled: If the device tree says
+status = "disabled" for the MDIO bus, we shouldn't need an MDIO bus at all.
+Instead, just exit as early as possible and do not call any MDIO API.
+
+The use of ds->user_mii_bus is inappropriate when the MDIO bus of the
+switch is described on the device tree [1], which is why we don't populate
+ds->user_mii_bus in that case.
+
+Link: https://lore.kernel.org/netdev/20231213120656.x46fyad6ls7sqyzv@skbuf/ [1]
+Suggested-by: David Bauer <mail@david-bauer.net>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240122053431.7751-1-arinc.unal@arinc9.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 34 ++++++++++++++++++++++++++--------
+ 1 file changed, 26 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2345,24 +2345,40 @@ mt7530_free_irq_common(struct mt7530_pri
+ static void
+ mt7530_free_irq(struct mt7530_priv *priv)
+ {
+-      mt7530_free_mdio_irq(priv);
++      struct device_node *mnp, *np = priv->dev->of_node;
++
++      mnp = of_get_child_by_name(np, "mdio");
++      if (!mnp)
++              mt7530_free_mdio_irq(priv);
++      of_node_put(mnp);
++
+       mt7530_free_irq_common(priv);
+ }
+ static int
+ mt7530_setup_mdio(struct mt7530_priv *priv)
+ {
++      struct device_node *mnp, *np = priv->dev->of_node;
+       struct dsa_switch *ds = priv->ds;
+       struct device *dev = priv->dev;
+       struct mii_bus *bus;
+       static int idx;
+-      int ret;
++      int ret = 0;
++
++      mnp = of_get_child_by_name(np, "mdio");
++
++      if (mnp && !of_device_is_available(mnp))
++              goto out;
+       bus = devm_mdiobus_alloc(dev);
+-      if (!bus)
+-              return -ENOMEM;
++      if (!bus) {
++              ret = -ENOMEM;
++              goto out;
++      }
++
++      if (!mnp)
++              ds->slave_mii_bus = bus;
+-      ds->slave_mii_bus = bus;
+       bus->priv = priv;
+       bus->name = KBUILD_MODNAME "-mii";
+       snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++);
+@@ -2373,16 +2389,18 @@ mt7530_setup_mdio(struct mt7530_priv *pr
+       bus->parent = dev;
+       bus->phy_mask = ~ds->phys_mii_mask;
+-      if (priv->irq)
++      if (priv->irq && !mnp)
+               mt7530_setup_mdio_irq(priv);
+-      ret = devm_mdiobus_register(dev, bus);
++      ret = devm_of_mdiobus_register(dev, bus, mnp);
+       if (ret) {
+               dev_err(dev, "failed to register MDIO bus: %d\n", ret);
+-              if (priv->irq)
++              if (priv->irq && !mnp)
+                       mt7530_free_mdio_irq(priv);
+       }
++out:
++      of_node_put(mnp);
+       return ret;
+ }
diff --git a/target/linux/generic/backport-6.6/790-05-v6.9-net-dsa-mt7530-always-trap-frames-to-active-CPU-port.patch b/target/linux/generic/backport-6.6/790-05-v6.9-net-dsa-mt7530-always-trap-frames-to-active-CPU-port.patch
new file mode 100644 (file)
index 0000000..8c73ea9
--- /dev/null
@@ -0,0 +1,125 @@
+From 617b07e08bcb1f69a72a085a7d847d1ca2999830 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Jan 2024 08:35:52 +0300
+Subject: [PATCH 05/30] net: dsa: mt7530: always trap frames to active CPU port
+ on MT7530
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+On the MT7530 switch, the CPU_PORT field indicates which CPU port to trap
+frames to, regardless of the affinity of the inbound user port.
+
+When multiple CPU ports are in use, if the DSA conduit interface is down,
+trapped frames won't be passed to the conduit interface.
+
+To make trapping frames work including this case, implement
+ds->ops->conduit_state_change() on this subdriver and set the CPU_PORT
+field to the numerically smallest CPU port whose conduit interface is up.
+Introduce the active_cpu_ports field to store the information of the active
+CPU ports. Correct the macros, CPU_PORT is bits 4 through 6 of the
+register.
+
+Add a comment to explain frame trapping for this switch.
+
+Currently, the driver doesn't support the use of multiple CPU ports so this
+is not necessarily a bug fix.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Suggested-by: Russell King (Oracle) <linux@armlinux.org.uk>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-1-042401f2b279@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 35 +++++++++++++++++++++++++++++++----
+ drivers/net/dsa/mt7530.h |  6 ++++--
+ 2 files changed, 35 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1232,10 +1232,6 @@ mt753x_cpu_port_enable(struct dsa_switch
+       mt7530_set(priv, MT7530_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) |
+                  UNU_FFP(BIT(port)));
+-      /* Set CPU port number */
+-      if (priv->id == ID_MT7530 || priv->id == ID_MT7621)
+-              mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port));
+-
+       /* Add the CPU port to the CPU port bitmap for MT7531 and the switch on
+        * the MT7988 SoC. Trapped frames will be forwarded to the CPU port that
+        * is affine to the inbound user port.
+@@ -3305,6 +3301,36 @@ static int mt753x_set_mac_eee(struct dsa
+       return 0;
+ }
++static void
++mt753x_conduit_state_change(struct dsa_switch *ds,
++                          const struct net_device *conduit,
++                          bool operational)
++{
++      struct dsa_port *cpu_dp = conduit->dsa_ptr;
++      struct mt7530_priv *priv = ds->priv;
++      int val = 0;
++      u8 mask;
++
++      /* Set the CPU port to trap frames to for MT7530. Trapped frames will be
++       * forwarded to the numerically smallest CPU port whose conduit
++       * interface is up.
++       */
++      if (priv->id != ID_MT7530 && priv->id != ID_MT7621)
++              return;
++
++      mask = BIT(cpu_dp->index);
++
++      if (operational)
++              priv->active_cpu_ports |= mask;
++      else
++              priv->active_cpu_ports &= ~mask;
++
++      if (priv->active_cpu_ports)
++              val = CPU_EN | CPU_PORT(__ffs(priv->active_cpu_ports));
++
++      mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val);
++}
++
+ static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
+ {
+       return 0;
+@@ -3360,6 +3386,7 @@ const struct dsa_switch_ops mt7530_switc
+       .phylink_mac_link_up    = mt753x_phylink_mac_link_up,
+       .get_mac_eee            = mt753x_get_mac_eee,
+       .set_mac_eee            = mt753x_set_mac_eee,
++      .master_state_change    = mt753x_conduit_state_change,
+ };
+ EXPORT_SYMBOL_GPL(mt7530_switch_ops);
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -45,8 +45,8 @@ enum mt753x_id {
+ #define  UNU_FFP(x)                   (((x) & 0xff) << 8)
+ #define  UNU_FFP_MASK                 UNU_FFP(~0)
+ #define  CPU_EN                               BIT(7)
+-#define  CPU_PORT(x)                  ((x) << 4)
+-#define  CPU_MASK                     (0xf << 4)
++#define  CPU_PORT_MASK                        GENMASK(6, 4)
++#define  CPU_PORT(x)                  FIELD_PREP(CPU_PORT_MASK, x)
+ #define  MIRROR_EN                    BIT(3)
+ #define  MIRROR_PORT(x)                       ((x) & 0x7)
+ #define  MIRROR_MASK                  0x7
+@@ -790,6 +790,7 @@ struct mt753x_info {
+  * @irq_domain:               IRQ domain of the switch irq_chip
+  * @irq_enable:               IRQ enable bits, synced to SYS_INT_EN
+  * @create_sgmii:     Pointer to function creating SGMII PCS instance(s)
++ * @active_cpu_ports: Holding the active CPU ports
+  */
+ struct mt7530_priv {
+       struct device           *dev;
+@@ -816,6 +817,7 @@ struct mt7530_priv {
+       struct irq_domain *irq_domain;
+       u32 irq_enable;
+       int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii);
++      u8 active_cpu_ports;
+ };
+ struct mt7530_hw_vlan_entry {
diff --git a/target/linux/generic/backport-6.6/790-06-v6.9-net-dsa-mt7530-use-p5_interface_select-as-data-type-.patch b/target/linux/generic/backport-6.6/790-06-v6.9-net-dsa-mt7530-use-p5_interface_select-as-data-type-.patch
new file mode 100644 (file)
index 0000000..3956ae4
--- /dev/null
@@ -0,0 +1,45 @@
+From 07f411e26f82d75723df1c0c072e5602d06f4e30 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Jan 2024 08:35:53 +0300
+Subject: [PATCH 06/30] net: dsa: mt7530: use p5_interface_select as data type
+ for p5_intf_sel
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Use the p5_interface_select enumeration as the data type for the
+p5_intf_sel field. This ensures p5_intf_sel can only take the values
+defined in the p5_interface_select enumeration.
+
+Remove the explicit assignment of 0 to P5_DISABLED as the first enum item
+is automatically assigned 0.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-2-042401f2b279@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -713,7 +713,7 @@ struct mt7530_port {
+ /* Port 5 interface select definitions */
+ enum p5_interface_select {
+-      P5_DISABLED = 0,
++      P5_DISABLED,
+       P5_INTF_SEL_PHY_P0,
+       P5_INTF_SEL_PHY_P4,
+       P5_INTF_SEL_GMAC5,
+@@ -806,7 +806,7 @@ struct mt7530_priv {
+       bool                    mcm;
+       phy_interface_t         p6_interface;
+       phy_interface_t         p5_interface;
+-      unsigned int            p5_intf_sel;
++      enum p5_interface_select p5_intf_sel;
+       u8                      mirror_rx;
+       u8                      mirror_tx;
+       struct mt7530_port      ports[MT7530_NUM_PORTS];
diff --git a/target/linux/generic/backport-6.6/790-07-v6.9-net-dsa-mt7530-store-port-5-SGMII-capability-of-MT75.patch b/target/linux/generic/backport-6.6/790-07-v6.9-net-dsa-mt7530-store-port-5-SGMII-capability-of-MT75.patch
new file mode 100644 (file)
index 0000000..426a7bc
--- /dev/null
@@ -0,0 +1,227 @@
+From 8f7db12efc189eedd196ed8d053236ce27add484 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Jan 2024 08:35:54 +0300
+Subject: [PATCH 07/30] net: dsa: mt7530: store port 5 SGMII capability of
+ MT7531
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Introduce the p5_sgmii field to store the information for whether port 5
+has got SGMII or not. Instead of reading the MT7531_TOP_SIG_SR register
+multiple times, the register will be read once and the value will be
+stored on the p5_sgmii field. This saves unnecessary reads of the
+register.
+
+Move the comment about MT7531AE and MT7531BE to mt7531_setup(), where the
+switch is identified.
+
+Get rid of mt7531_dual_sgmii_supported() now that priv->p5_sgmii stores the
+information. Address the code where mt7531_dual_sgmii_supported() is used.
+
+Get rid of mt7531_is_rgmii_port() which just prints the opposite of
+priv->p5_sgmii.
+
+Instead of calling mt7531_pll_setup() then returning, do not call it if
+port 5 is SGMII.
+
+Remove P5_INTF_SEL_GMAC5_SGMII. The p5_interface_select enum is supposed to
+represent the mode that port 5 is being used in, not the hardware
+information of port 5. Set p5_intf_sel to P5_INTF_SEL_GMAC5 instead, if
+port 5 is not dsa_is_unused_port().
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-3-042401f2b279@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530-mdio.c |  7 ++---
+ drivers/net/dsa/mt7530.c      | 48 ++++++++++++-----------------------
+ drivers/net/dsa/mt7530.h      |  6 +++--
+ 3 files changed, 22 insertions(+), 39 deletions(-)
+
+--- a/drivers/net/dsa/mt7530-mdio.c
++++ b/drivers/net/dsa/mt7530-mdio.c
+@@ -81,17 +81,14 @@ static const struct regmap_bus mt7530_re
+ };
+ static int
+-mt7531_create_sgmii(struct mt7530_priv *priv, bool dual_sgmii)
++mt7531_create_sgmii(struct mt7530_priv *priv)
+ {
+       struct regmap_config *mt7531_pcs_config[2] = {};
+       struct phylink_pcs *pcs;
+       struct regmap *regmap;
+       int i, ret = 0;
+-      /* MT7531AE has two SGMII units for port 5 and port 6
+-       * MT7531BE has only one SGMII unit for port 6
+-       */
+-      for (i = dual_sgmii ? 0 : 1; i < 2; i++) {
++      for (i = priv->p5_sgmii ? 0 : 1; i < 2; i++) {
+               mt7531_pcs_config[i] = devm_kzalloc(priv->dev,
+                                                   sizeof(struct regmap_config),
+                                                   GFP_KERNEL);
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -487,15 +487,6 @@ mt7530_pad_clk_setup(struct dsa_switch *
+       return 0;
+ }
+-static bool mt7531_dual_sgmii_supported(struct mt7530_priv *priv)
+-{
+-      u32 val;
+-
+-      val = mt7530_read(priv, MT7531_TOP_SIG_SR);
+-
+-      return (val & PAD_DUAL_SGMII_EN) != 0;
+-}
+-
+ static int
+ mt7531_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
+ {
+@@ -510,9 +501,6 @@ mt7531_pll_setup(struct mt7530_priv *pri
+       u32 xtal;
+       u32 val;
+-      if (mt7531_dual_sgmii_supported(priv))
+-              return;
+-
+       val = mt7530_read(priv, MT7531_CREV);
+       top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR);
+       hwstrap = mt7530_read(priv, MT7531_HWTRAP);
+@@ -920,8 +908,6 @@ static const char *p5_intf_modes(unsigne
+               return "PHY P4";
+       case P5_INTF_SEL_GMAC5:
+               return "GMAC5";
+-      case P5_INTF_SEL_GMAC5_SGMII:
+-              return "GMAC5_SGMII";
+       default:
+               return "unknown";
+       }
+@@ -2694,6 +2680,12 @@ mt7531_setup(struct dsa_switch *ds)
+               return -ENODEV;
+       }
++      /* MT7531AE has got two SGMII units. One for port 5, one for port 6.
++       * MT7531BE has got only one SGMII unit which is for port 6.
++       */
++      val = mt7530_read(priv, MT7531_TOP_SIG_SR);
++      priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN);
++
+       /* all MACs must be forced link-down before sw reset */
+       for (i = 0; i < MT7530_NUM_PORTS; i++)
+               mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK);
+@@ -2703,21 +2695,18 @@ mt7531_setup(struct dsa_switch *ds)
+                    SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
+                    SYS_CTRL_REG_RST);
+-      mt7531_pll_setup(priv);
+-
+-      if (mt7531_dual_sgmii_supported(priv)) {
+-              priv->p5_intf_sel = P5_INTF_SEL_GMAC5_SGMII;
+-
++      if (!priv->p5_sgmii) {
++              mt7531_pll_setup(priv);
++      } else {
+               /* Let ds->slave_mii_bus be able to access external phy. */
+               mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO11_RG_RXD2_MASK,
+                          MT7531_EXT_P_MDC_11);
+               mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO12_RG_RXD3_MASK,
+                          MT7531_EXT_P_MDIO_12);
+-      } else {
+-              priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
+       }
+-      dev_dbg(ds->dev, "P5 support %s interface\n",
+-              p5_intf_modes(priv->p5_intf_sel));
++
++      if (!dsa_is_unused_port(ds, 5))
++              priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
+       mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK,
+                  MT7531_GPIO0_INTERRUPT);
+@@ -2784,11 +2773,6 @@ static void mt7530_mac_port_get_caps(str
+       }
+ }
+-static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port)
+-{
+-      return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII);
+-}
+-
+ static void mt7531_mac_port_get_caps(struct dsa_switch *ds, int port,
+                                    struct phylink_config *config)
+ {
+@@ -2801,7 +2785,7 @@ static void mt7531_mac_port_get_caps(str
+               break;
+       case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */
+-              if (mt7531_is_rgmii_port(priv, port)) {
++              if (!priv->p5_sgmii) {
+                       phy_interface_set_rgmii(config->supported_interfaces);
+                       break;
+               }
+@@ -2868,7 +2852,7 @@ static int mt7531_rgmii_setup(struct mt7
+ {
+       u32 val;
+-      if (!mt7531_is_rgmii_port(priv, port)) {
++      if (priv->p5_sgmii) {
+               dev_err(priv->dev, "RGMII mode is not available for port %d\n",
+                       port);
+               return -EINVAL;
+@@ -3111,7 +3095,7 @@ mt7531_cpu_port_config(struct dsa_switch
+       switch (port) {
+       case 5:
+-              if (mt7531_is_rgmii_port(priv, port))
++              if (!priv->p5_sgmii)
+                       interface = PHY_INTERFACE_MODE_RGMII;
+               else
+                       interface = PHY_INTERFACE_MODE_2500BASEX;
+@@ -3263,7 +3247,7 @@ mt753x_setup(struct dsa_switch *ds)
+               mt7530_free_irq_common(priv);
+       if (priv->create_sgmii) {
+-              ret = priv->create_sgmii(priv, mt7531_dual_sgmii_supported(priv));
++              ret = priv->create_sgmii(priv);
+               if (ret && priv->irq)
+                       mt7530_free_irq(priv);
+       }
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -717,7 +717,6 @@ enum p5_interface_select {
+       P5_INTF_SEL_PHY_P0,
+       P5_INTF_SEL_PHY_P4,
+       P5_INTF_SEL_GMAC5,
+-      P5_INTF_SEL_GMAC5_SGMII,
+ };
+ struct mt7530_priv;
+@@ -786,6 +785,8 @@ struct mt753x_info {
+  *                    registers
+  * @p6_interface      Holding the current port 6 interface
+  * @p5_intf_sel:      Holding the current port 5 interface select
++ * @p5_sgmii:         Flag for distinguishing if port 5 of the MT7531 switch
++ *                    has got SGMII
+  * @irq:              IRQ number of the switch
+  * @irq_domain:               IRQ domain of the switch irq_chip
+  * @irq_enable:               IRQ enable bits, synced to SYS_INT_EN
+@@ -807,6 +808,7 @@ struct mt7530_priv {
+       phy_interface_t         p6_interface;
+       phy_interface_t         p5_interface;
+       enum p5_interface_select p5_intf_sel;
++      bool                    p5_sgmii;
+       u8                      mirror_rx;
+       u8                      mirror_tx;
+       struct mt7530_port      ports[MT7530_NUM_PORTS];
+@@ -816,7 +818,7 @@ struct mt7530_priv {
+       int irq;
+       struct irq_domain *irq_domain;
+       u32 irq_enable;
+-      int (*create_sgmii)(struct mt7530_priv *priv, bool dual_sgmii);
++      int (*create_sgmii)(struct mt7530_priv *priv);
+       u8 active_cpu_ports;
+ };
diff --git a/target/linux/generic/backport-6.6/790-08-v6.9-net-dsa-mt7530-improve-comments-regarding-switch-por.patch b/target/linux/generic/backport-6.6/790-08-v6.9-net-dsa-mt7530-improve-comments-regarding-switch-por.patch
new file mode 100644 (file)
index 0000000..3309e24
--- /dev/null
@@ -0,0 +1,133 @@
+From c91b7fb8fbb2e18ebb497e67f4252cec78e3a29b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Jan 2024 08:35:55 +0300
+Subject: [PATCH 08/30] net: dsa: mt7530: improve comments regarding switch
+ ports
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There's no logic to numerically order the CPU ports. Just state the port
+number instead.
+
+Remove the irrelevant PHY muxing information from
+mt7530_mac_port_get_caps(). Explain the supported MII modes instead.
+
+Remove the out of place PHY muxing information from
+mt753x_phylink_mac_config(). The function is for MT7530, MT7531, and the
+switch on the MT7988 SoC but there's no PHY muxing on MT7531 or the switch
+on the MT7988 SoC.
+
+These comments were gradually introduced with the commits below.
+commit ca366d6c889b ("net: dsa: mt7530: Convert to PHYLINK API")
+commit 38f790a80560 ("net: dsa: mt7530: Add support for port 5")
+commit 88bdef8be9f6 ("net: dsa: mt7530: Extend device data ready for adding
+a new hardware")
+commit c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch")
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-4-042401f2b279@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 30 ++++++++++++++++++++----------
+ 1 file changed, 20 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2751,12 +2751,14 @@ static void mt7530_mac_port_get_caps(str
+                                    struct phylink_config *config)
+ {
+       switch (port) {
+-      case 0 ... 4: /* Internal phy */
++      /* Ports which are connected to switch PHYs. There is no MII pinout. */
++      case 0 ... 4:
+               __set_bit(PHY_INTERFACE_MODE_GMII,
+                         config->supported_interfaces);
+               break;
+-      case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
++      /* Port 5 supports rgmii with delays, mii, and gmii. */
++      case 5:
+               phy_interface_set_rgmii(config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_MII,
+                         config->supported_interfaces);
+@@ -2764,7 +2766,8 @@ static void mt7530_mac_port_get_caps(str
+                         config->supported_interfaces);
+               break;
+-      case 6: /* 1st cpu port */
++      /* Port 6 supports rgmii and trgmii. */
++      case 6:
+               __set_bit(PHY_INTERFACE_MODE_RGMII,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_TRGMII,
+@@ -2779,19 +2782,24 @@ static void mt7531_mac_port_get_caps(str
+       struct mt7530_priv *priv = ds->priv;
+       switch (port) {
+-      case 0 ... 4: /* Internal phy */
++      /* Ports which are connected to switch PHYs. There is no MII pinout. */
++      case 0 ... 4:
+               __set_bit(PHY_INTERFACE_MODE_GMII,
+                         config->supported_interfaces);
+               break;
+-      case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */
++      /* Port 5 supports rgmii with delays on MT7531BE, sgmii/802.3z on
++       * MT7531AE.
++       */
++      case 5:
+               if (!priv->p5_sgmii) {
+                       phy_interface_set_rgmii(config->supported_interfaces);
+                       break;
+               }
+               fallthrough;
+-      case 6: /* 1st cpu port supports sgmii/8023z only */
++      /* Port 6 supports sgmii/802.3z. */
++      case 6:
+               __set_bit(PHY_INTERFACE_MODE_SGMII,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+@@ -2810,11 +2818,13 @@ static void mt7988_mac_port_get_caps(str
+       phy_interface_zero(config->supported_interfaces);
+       switch (port) {
+-      case 0 ... 4: /* Internal phy */
++      /* Ports which are connected to switch PHYs. There is no MII pinout. */
++      case 0 ... 4:
+               __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+                         config->supported_interfaces);
+               break;
++      /* Port 6 is connected to SoC's XGMII MAC. There is no MII pinout. */
+       case 6:
+               __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+                         config->supported_interfaces);
+@@ -2978,12 +2988,12 @@ mt753x_phylink_mac_config(struct dsa_swi
+       u32 mcr_cur, mcr_new;
+       switch (port) {
+-      case 0 ... 4: /* Internal phy */
++      case 0 ... 4:
+               if (state->interface != PHY_INTERFACE_MODE_GMII &&
+                   state->interface != PHY_INTERFACE_MODE_INTERNAL)
+                       goto unsupported;
+               break;
+-      case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */
++      case 5:
+               if (priv->p5_interface == state->interface)
+                       break;
+@@ -2993,7 +3003,7 @@ mt753x_phylink_mac_config(struct dsa_swi
+               if (priv->p5_intf_sel != P5_DISABLED)
+                       priv->p5_interface = state->interface;
+               break;
+-      case 6: /* 1st cpu port */
++      case 6:
+               if (priv->p6_interface == state->interface)
+                       break;
diff --git a/target/linux/generic/backport-6.6/790-09-v6.9-net-dsa-mt7530-improve-code-path-for-setting-up-port.patch b/target/linux/generic/backport-6.6/790-09-v6.9-net-dsa-mt7530-improve-code-path-for-setting-up-port.patch
new file mode 100644 (file)
index 0000000..9d1b155
--- /dev/null
@@ -0,0 +1,95 @@
+From c1b2294a9b4b9b6c0cbe58666cb86e0a9cb0abfd Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Jan 2024 08:35:56 +0300
+Subject: [PATCH 09/30] net: dsa: mt7530: improve code path for setting up port
+ 5
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There're two code paths for setting up port 5:
+
+mt7530_setup()
+-> mt7530_setup_port5()
+
+mt753x_phylink_mac_config()
+-> mt753x_mac_config()
+   -> mt7530_mac_config()
+      -> mt7530_setup_port5()
+
+Currently mt7530_setup_port5() from mt7530_setup() always runs. If port 5
+is used as a CPU, DSA, or user port, mt7530_setup_port5() from
+mt753x_phylink_mac_config() won't run. That is because priv->p5_interface
+set on mt7530_setup_port5() will match state->interface on
+mt753x_phylink_mac_config() which will stop running mt7530_setup_port5()
+again.
+
+Therefore, mt7530_setup_port5() will never run from
+mt753x_phylink_mac_config().
+
+Address this by not running mt7530_setup_port5() from mt7530_setup() if
+port 5 is used as a CPU, DSA, or user port. This driver isn't in the
+dsa_switches_apply_workarounds[] array so phylink will always be present.
+
+To keep the cases where port 5 isn't controlled by phylink working as
+before, preserve the mt7530_setup_port5() call from mt7530_setup().
+
+Do not set priv->p5_intf_sel to P5_DISABLED. It is already set to that when
+"priv" is allocated.
+
+Move setting the interface to a more specific location. It's supposed to be
+overwritten if PHY muxing is detected.
+
+Improve the comment which explains the process.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-5-042401f2b279@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 17 ++++++++---------
+ 1 file changed, 8 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2529,16 +2529,15 @@ mt7530_setup(struct dsa_switch *ds)
+               return ret;
+       /* Setup port 5 */
+-      priv->p5_intf_sel = P5_DISABLED;
+-      interface = PHY_INTERFACE_MODE_NA;
+-
+       if (!dsa_is_unused_port(ds, 5)) {
+               priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
+-              ret = of_get_phy_mode(dsa_to_port(ds, 5)->dn, &interface);
+-              if (ret && ret != -ENODEV)
+-                      return ret;
+       } else {
+-              /* Scan the ethernet nodes. look for GMAC1, lookup used phy */
++              /* Scan the ethernet nodes. Look for GMAC1, lookup the used PHY.
++               * Set priv->p5_intf_sel to the appropriate value if PHY muxing
++               * is detected.
++               */
++              interface = PHY_INTERFACE_MODE_NA;
++
+               for_each_child_of_node(dn, mac_np) {
+                       if (!of_device_is_compatible(mac_np,
+                                                    "mediatek,eth-mac"))
+@@ -2569,6 +2568,8 @@ mt7530_setup(struct dsa_switch *ds)
+                       of_node_put(phy_node);
+                       break;
+               }
++
++              mt7530_setup_port5(ds, interface);
+       }
+ #ifdef CONFIG_GPIOLIB
+@@ -2579,8 +2580,6 @@ mt7530_setup(struct dsa_switch *ds)
+       }
+ #endif /* CONFIG_GPIOLIB */
+-      mt7530_setup_port5(ds, interface);
+-
+       /* Flush the FDB table */
+       ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL);
+       if (ret < 0)
diff --git a/target/linux/generic/backport-6.6/790-10-v6.9-net-dsa-mt7530-do-not-set-priv-p5_interface-on-mt753.patch b/target/linux/generic/backport-6.6/790-10-v6.9-net-dsa-mt7530-do-not-set-priv-p5_interface-on-mt753.patch
new file mode 100644 (file)
index 0000000..0b021f0
--- /dev/null
@@ -0,0 +1,42 @@
+From cd1cee68e57eedb460a68d1f42abf9f740b17e94 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Jan 2024 08:35:57 +0300
+Subject: [PATCH 10/30] net: dsa: mt7530: do not set priv->p5_interface on
+ mt7530_setup_port5()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Running mt7530_setup_port5() from mt7530_setup() used to handle all cases
+of configuring port 5, including phylink.
+
+Setting priv->p5_interface under mt7530_setup_port5() makes sure that
+mt7530_setup_port5() from mt753x_phylink_mac_config() won't run.
+
+The commit ("net: dsa: mt7530: improve code path for setting up port 5")
+makes so that mt7530_setup_port5() from mt7530_setup() runs only on
+non-phylink cases.
+
+Get rid of unnecessarily setting priv->p5_interface under
+mt7530_setup_port5() as port 5 phylink configuration will be done by
+running mt7530_setup_port5() from mt753x_phylink_mac_config() now.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-6-042401f2b279@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -978,8 +978,6 @@ static void mt7530_setup_port5(struct ds
+       dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n",
+               val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface));
+-      priv->p5_interface = interface;
+-
+ unlock_exit:
+       mutex_unlock(&priv->reg_mutex);
+ }
diff --git a/target/linux/generic/backport-6.6/790-11-v6.9-net-dsa-mt7530-do-not-run-mt7530_setup_port5-if-port.patch b/target/linux/generic/backport-6.6/790-11-v6.9-net-dsa-mt7530-do-not-run-mt7530_setup_port5-if-port.patch
new file mode 100644 (file)
index 0000000..4f93d37
--- /dev/null
@@ -0,0 +1,62 @@
+From e55a68aeb0f8b9c74b582b7a5e92b82988832bf8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Jan 2024 08:35:58 +0300
+Subject: [PATCH 11/30] net: dsa: mt7530: do not run mt7530_setup_port5() if
+ port 5 is disabled
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There's no need to run all the code on mt7530_setup_port5() if port 5 is
+disabled. The only case for calling mt7530_setup_port5() from
+mt7530_setup() is when PHY muxing is enabled. That is because port 5 is not
+defined as a port on the devicetree, therefore, it cannot be controlled by
+phylink.
+
+Because of this, run mt7530_setup_port5() if priv->p5_intf_sel is
+P5_INTF_SEL_PHY_P0 or P5_INTF_SEL_PHY_P4. Remove the P5_DISABLED case from
+mt7530_setup_port5().
+
+Stop initialising the interface variable as the remaining cases will always
+call mt7530_setup_port5() with it initialised.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240122-for-netnext-mt7530-improvements-1-v3-7-042401f2b279@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -942,9 +942,6 @@ static void mt7530_setup_port5(struct ds
+               /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */
+               val &= ~MHWTRAP_P5_DIS;
+               break;
+-      case P5_DISABLED:
+-              interface = PHY_INTERFACE_MODE_NA;
+-              break;
+       default:
+               dev_err(ds->dev, "Unsupported p5_intf_sel %d\n",
+                       priv->p5_intf_sel);
+@@ -2534,8 +2531,6 @@ mt7530_setup(struct dsa_switch *ds)
+                * Set priv->p5_intf_sel to the appropriate value if PHY muxing
+                * is detected.
+                */
+-              interface = PHY_INTERFACE_MODE_NA;
+-
+               for_each_child_of_node(dn, mac_np) {
+                       if (!of_device_is_compatible(mac_np,
+                                                    "mediatek,eth-mac"))
+@@ -2567,7 +2562,9 @@ mt7530_setup(struct dsa_switch *ds)
+                       break;
+               }
+-              mt7530_setup_port5(ds, interface);
++              if (priv->p5_intf_sel == P5_INTF_SEL_PHY_P0 ||
++                  priv->p5_intf_sel == P5_INTF_SEL_PHY_P4)
++                      mt7530_setup_port5(ds, interface);
+       }
+ #ifdef CONFIG_GPIOLIB
diff --git a/target/linux/generic/backport-6.6/790-12-v6.9-net-dsa-mt7530-empty-default-case-on-mt7530_setup_po.patch b/target/linux/generic/backport-6.6/790-12-v6.9-net-dsa-mt7530-empty-default-case-on-mt7530_setup_po.patch
new file mode 100644 (file)
index 0000000..c9a57e3
--- /dev/null
@@ -0,0 +1,58 @@
+From 1f538cda24bcb69919da2fcac0211b66281d3d4e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Tue, 6 Feb 2024 01:08:02 +0300
+Subject: [PATCH 12/30] net: dsa: mt7530: empty default case on
+ mt7530_setup_port5()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There're two code paths for setting up port 5:
+
+mt7530_setup()
+-> mt7530_setup_port5()
+
+mt753x_phylink_mac_config()
+-> mt753x_mac_config()
+   -> mt7530_mac_config()
+      -> mt7530_setup_port5()
+
+On the first code path, priv->p5_intf_sel is either set to
+P5_INTF_SEL_PHY_P0 or P5_INTF_SEL_PHY_P4 when mt7530_setup_port5() is run.
+
+On the second code path, priv->p5_intf_sel is set to P5_INTF_SEL_GMAC5 when
+mt7530_setup_port5() is run.
+
+Empty the default case which will never run but is needed nonetheless to
+handle all the remaining enumeration values.
+
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-1-d7d92a185cb1@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -943,9 +943,7 @@ static void mt7530_setup_port5(struct ds
+               val &= ~MHWTRAP_P5_DIS;
+               break;
+       default:
+-              dev_err(ds->dev, "Unsupported p5_intf_sel %d\n",
+-                      priv->p5_intf_sel);
+-              goto unlock_exit;
++              break;
+       }
+       /* Setup RGMII settings */
+@@ -975,7 +973,6 @@ static void mt7530_setup_port5(struct ds
+       dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n",
+               val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface));
+-unlock_exit:
+       mutex_unlock(&priv->reg_mutex);
+ }
diff --git a/target/linux/generic/backport-6.6/790-13-v6.9-net-dsa-mt7530-move-XTAL-check-to-mt7530_setup.patch b/target/linux/generic/backport-6.6/790-13-v6.9-net-dsa-mt7530-move-XTAL-check-to-mt7530_setup.patch
new file mode 100644 (file)
index 0000000..8058257
--- /dev/null
@@ -0,0 +1,53 @@
+From 12c511cd31c2dc6bd96e4a89f7709d515aa8a76b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Tue, 6 Feb 2024 01:08:03 +0300
+Subject: [PATCH 13/30] net: dsa: mt7530: move XTAL check to mt7530_setup()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The crystal frequency concerns the switch core. The frequency should be
+checked when the switch is being set up so the driver can reject the
+unsupported hardware earlier and without requiring port 6 to be used.
+
+Move it to mt7530_setup(). Drop the unnecessary function printing.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-2-d7d92a185cb1@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -422,13 +422,6 @@ mt7530_pad_clk_setup(struct dsa_switch *
+       xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK;
+-      if (xtal == HWTRAP_XTAL_20MHZ) {
+-              dev_err(priv->dev,
+-                      "%s: MT7530 with a 20MHz XTAL is not supported!\n",
+-                      __func__);
+-              return -EINVAL;
+-      }
+-
+       switch (interface) {
+       case PHY_INTERFACE_MODE_RGMII:
+               trgint = 0;
+@@ -2458,6 +2451,12 @@ mt7530_setup(struct dsa_switch *ds)
+               return -ENODEV;
+       }
++      if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_20MHZ) {
++              dev_err(priv->dev,
++                      "MT7530 with a 20MHz XTAL is not supported!\n");
++              return -EINVAL;
++      }
++
+       /* Reset the switch through internal reset */
+       mt7530_write(priv, MT7530_SYS_CTRL,
+                    SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
diff --git a/target/linux/generic/backport-6.6/790-14-v6.9-net-dsa-mt7530-simplify-mt7530_pad_clk_setup.patch b/target/linux/generic/backport-6.6/790-14-v6.9-net-dsa-mt7530-simplify-mt7530_pad_clk_setup.patch
new file mode 100644 (file)
index 0000000..75c90b9
--- /dev/null
@@ -0,0 +1,146 @@
+From c33899a6a8c1a5723afbfc075600aba2e2bdbea7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Tue, 6 Feb 2024 01:08:04 +0300
+Subject: [PATCH 14/30] net: dsa: mt7530: simplify mt7530_pad_clk_setup()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This code is from before this driver was converted to phylink API. Phylink
+deals with the unsupported interface cases before mt7530_pad_clk_setup() is
+run. Therefore, the default case would never run. However, it must be
+defined nonetheless to handle all the remaining enumeration values, the
+phy-modes.
+
+Switch to if statement for RGMII and return which simplifies the code and
+saves an indent.
+
+Set P6_INTF_MODE, which is the three least significant bits of the
+MT7530_P6ECR register, to 0 for RGMII even though it will already be 0
+after reset. This is to keep supporting dynamic reconfiguration of the port
+in the case the interface changes from TRGMII to RGMII.
+
+Disable the TRGMII clocks for all cases. They will be enabled if TRGMII is
+being used.
+
+Read XTAL after checking for RGMII as it's only needed for the TRGMII
+interface mode.
+
+Reviewed-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-3-d7d92a185cb1@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 91 ++++++++++++++++++----------------------
+ 1 file changed, 40 insertions(+), 51 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -418,65 +418,54 @@ static int
+ mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
+ {
+       struct mt7530_priv *priv = ds->priv;
+-      u32 ncpo1, ssc_delta, trgint, xtal;
++      u32 ncpo1, ssc_delta, xtal;
+-      xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK;
++      /* Disable the MT7530 TRGMII clocks */
++      core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
+-      switch (interface) {
+-      case PHY_INTERFACE_MODE_RGMII:
+-              trgint = 0;
+-              break;
+-      case PHY_INTERFACE_MODE_TRGMII:
+-              trgint = 1;
+-              if (xtal == HWTRAP_XTAL_25MHZ)
+-                      ssc_delta = 0x57;
+-              else
+-                      ssc_delta = 0x87;
+-              if (priv->id == ID_MT7621) {
+-                      /* PLL frequency: 125MHz: 1.0GBit */
+-                      if (xtal == HWTRAP_XTAL_40MHZ)
+-                              ncpo1 = 0x0640;
+-                      if (xtal == HWTRAP_XTAL_25MHZ)
+-                              ncpo1 = 0x0a00;
+-              } else { /* PLL frequency: 250MHz: 2.0Gbit */
+-                      if (xtal == HWTRAP_XTAL_40MHZ)
+-                              ncpo1 = 0x0c80;
+-                      if (xtal == HWTRAP_XTAL_25MHZ)
+-                              ncpo1 = 0x1400;
+-              }
+-              break;
+-      default:
+-              dev_err(priv->dev, "xMII interface %d not supported\n",
+-                      interface);
+-              return -EINVAL;
++      if (interface == PHY_INTERFACE_MODE_RGMII) {
++              mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK,
++                         P6_INTF_MODE(0));
++              return 0;
+       }
+-      mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK,
+-                 P6_INTF_MODE(trgint));
++      mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1));
+-      if (trgint) {
+-              /* Disable the MT7530 TRGMII clocks */
+-              core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
+-
+-              /* Setup the MT7530 TRGMII Tx Clock */
+-              core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1));
+-              core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0));
+-              core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta));
+-              core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta));
+-              core_write(priv, CORE_PLL_GROUP4,
+-                         RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN |
+-                         RG_SYSPLL_BIAS_LPF_EN);
+-              core_write(priv, CORE_PLL_GROUP2,
+-                         RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN |
+-                         RG_SYSPLL_POSDIV(1));
+-              core_write(priv, CORE_PLL_GROUP7,
+-                         RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) |
+-                         RG_LCDDS_PWDB | RG_LCDDS_ISO_EN);
++      xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK;
+-              /* Enable the MT7530 TRGMII clocks */
+-              core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
++      if (xtal == HWTRAP_XTAL_25MHZ)
++              ssc_delta = 0x57;
++      else
++              ssc_delta = 0x87;
++
++      if (priv->id == ID_MT7621) {
++              /* PLL frequency: 125MHz: 1.0GBit */
++              if (xtal == HWTRAP_XTAL_40MHZ)
++                      ncpo1 = 0x0640;
++              if (xtal == HWTRAP_XTAL_25MHZ)
++                      ncpo1 = 0x0a00;
++      } else { /* PLL frequency: 250MHz: 2.0Gbit */
++              if (xtal == HWTRAP_XTAL_40MHZ)
++                      ncpo1 = 0x0c80;
++              if (xtal == HWTRAP_XTAL_25MHZ)
++                      ncpo1 = 0x1400;
+       }
++      /* Setup the MT7530 TRGMII Tx Clock */
++      core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1));
++      core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0));
++      core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta));
++      core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta));
++      core_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN |
++                 RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN);
++      core_write(priv, CORE_PLL_GROUP2, RG_SYSPLL_EN_NORMAL |
++                 RG_SYSPLL_VODEN | RG_SYSPLL_POSDIV(1));
++      core_write(priv, CORE_PLL_GROUP7, RG_LCDDS_PCW_NCPO_CHG |
++                 RG_LCCDS_C(3) | RG_LCDDS_PWDB | RG_LCDDS_ISO_EN);
++
++      /* Enable the MT7530 TRGMII clocks */
++      core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
++
+       return 0;
+ }
diff --git a/target/linux/generic/backport-6.6/790-15-v6.9-net-dsa-mt7530-call-port-6-setup-from-mt7530_mac_con.patch b/target/linux/generic/backport-6.6/790-15-v6.9-net-dsa-mt7530-call-port-6-setup-from-mt7530_mac_con.patch
new file mode 100644 (file)
index 0000000..330a92e
--- /dev/null
@@ -0,0 +1,97 @@
+From e612922de7070a28802216650ee88128a57290de Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Tue, 6 Feb 2024 01:08:05 +0300
+Subject: [PATCH 15/30] net: dsa: mt7530: call port 6 setup from
+ mt7530_mac_config()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+mt7530_pad_clk_setup() is called if port 6 is enabled. It used to do more
+things than setting up port 6. That part was moved to more appropriate
+locations, mt7530_setup() and mt7530_pll_setup().
+
+Now that all it does is set up port 6, rename it to mt7530_setup_port6(),
+and move it to a more appropriate location, under mt7530_mac_config().
+
+Change mt7530_setup_port6() to void as there're no error cases.
+
+Leave an empty mt7530_pad_clk_setup() to satisfy the pad_setup function
+pointer.
+
+This is the code path for setting up the ports before:
+
+dsa_switch_ops :: phylink_mac_config() -> mt753x_phylink_mac_config()
+-> mt753x_mac_config()
+   -> mt753x_info :: mac_port_config() -> mt7530_mac_config()
+      -> mt7530_setup_port5()
+-> mt753x_pad_setup()
+   -> mt753x_info :: pad_setup() -> mt7530_pad_clk_setup()
+
+This is after:
+
+dsa_switch_ops :: phylink_mac_config() -> mt753x_phylink_mac_config()
+-> mt753x_mac_config()
+   -> mt753x_info :: mac_port_config() -> mt7530_mac_config()
+      -> mt7530_setup_port5()
+      -> mt7530_setup_port6()
+
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-4-d7d92a185cb1@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 19 +++++++++++--------
+ 1 file changed, 11 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -414,8 +414,8 @@ mt753x_preferred_default_local_cpu_port(
+ }
+ /* Setup port 6 interface mode and TRGMII TX circuit */
+-static int
+-mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
++static void
++mt7530_setup_port6(struct dsa_switch *ds, phy_interface_t interface)
+ {
+       struct mt7530_priv *priv = ds->priv;
+       u32 ncpo1, ssc_delta, xtal;
+@@ -426,7 +426,7 @@ mt7530_pad_clk_setup(struct dsa_switch *
+       if (interface == PHY_INTERFACE_MODE_RGMII) {
+               mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK,
+                          P6_INTF_MODE(0));
+-              return 0;
++              return;
+       }
+       mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1));
+@@ -465,7 +465,11 @@ mt7530_pad_clk_setup(struct dsa_switch *
+       /* Enable the MT7530 TRGMII clocks */
+       core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
++}
++static int
++mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
++{
+       return 0;
+ }
+@@ -2826,11 +2830,10 @@ mt7530_mac_config(struct dsa_switch *ds,
+ {
+       struct mt7530_priv *priv = ds->priv;
+-      /* Only need to setup port5. */
+-      if (port != 5)
+-              return 0;
+-
+-      mt7530_setup_port5(priv->ds, interface);
++      if (port == 5)
++              mt7530_setup_port5(priv->ds, interface);
++      else if (port == 6)
++              mt7530_setup_port6(priv->ds, interface);
+       return 0;
+ }
diff --git a/target/linux/generic/backport-6.6/790-16-v6.9-net-dsa-mt7530-remove-pad_setup-function-pointer.patch b/target/linux/generic/backport-6.6/790-16-v6.9-net-dsa-mt7530-remove-pad_setup-function-pointer.patch
new file mode 100644 (file)
index 0000000..dcf1afa
--- /dev/null
@@ -0,0 +1,148 @@
+From af83e0c7d766078fcd5580c0c81b9e5b55ff5906 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Tue, 6 Feb 2024 01:08:06 +0300
+Subject: [PATCH 16/30] net: dsa: mt7530: remove pad_setup function pointer
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The pad_setup function pointer was introduced with 88bdef8be9f6 ("net: dsa:
+mt7530: Extend device data ready for adding a new hardware"). It was being
+used to set up the core clock and port 6 of the MT7530 switch, and pll of
+the MT7531 switch.
+
+All of these were moved to more appropriate locations, and it was never
+used for the switch on the MT7988 SoC. Therefore, this function pointer
+hasn't got a use anymore. Remove it.
+
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-5-d7d92a185cb1@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 36 ++----------------------------------
+ drivers/net/dsa/mt7530.h |  3 ---
+ 2 files changed, 2 insertions(+), 37 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -467,18 +467,6 @@ mt7530_setup_port6(struct dsa_switch *ds
+       core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN);
+ }
+-static int
+-mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
+-{
+-      return 0;
+-}
+-
+-static int
+-mt7531_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
+-{
+-      return 0;
+-}
+-
+ static void
+ mt7531_pll_setup(struct mt7530_priv *priv)
+ {
+@@ -2817,14 +2805,6 @@ static void mt7988_mac_port_get_caps(str
+ }
+ static int
+-mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state)
+-{
+-      struct mt7530_priv *priv = ds->priv;
+-
+-      return priv->info->pad_setup(ds, state->interface);
+-}
+-
+-static int
+ mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+                 phy_interface_t interface)
+ {
+@@ -2989,8 +2969,6 @@ mt753x_phylink_mac_config(struct dsa_swi
+               if (priv->p6_interface == state->interface)
+                       break;
+-              mt753x_pad_setup(ds, state);
+-
+               if (mt753x_mac_config(ds, port, mode, state) < 0)
+                       goto unsupported;
+@@ -3307,11 +3285,6 @@ mt753x_conduit_state_change(struct dsa_s
+       mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val);
+ }
+-static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
+-{
+-      return 0;
+-}
+-
+ static int mt7988_setup(struct dsa_switch *ds)
+ {
+       struct mt7530_priv *priv = ds->priv;
+@@ -3375,7 +3348,6 @@ const struct mt753x_info mt753x_table[]
+               .phy_write_c22 = mt7530_phy_write_c22,
+               .phy_read_c45 = mt7530_phy_read_c45,
+               .phy_write_c45 = mt7530_phy_write_c45,
+-              .pad_setup = mt7530_pad_clk_setup,
+               .mac_port_get_caps = mt7530_mac_port_get_caps,
+               .mac_port_config = mt7530_mac_config,
+       },
+@@ -3387,7 +3359,6 @@ const struct mt753x_info mt753x_table[]
+               .phy_write_c22 = mt7530_phy_write_c22,
+               .phy_read_c45 = mt7530_phy_read_c45,
+               .phy_write_c45 = mt7530_phy_write_c45,
+-              .pad_setup = mt7530_pad_clk_setup,
+               .mac_port_get_caps = mt7530_mac_port_get_caps,
+               .mac_port_config = mt7530_mac_config,
+       },
+@@ -3399,7 +3370,6 @@ const struct mt753x_info mt753x_table[]
+               .phy_write_c22 = mt7531_ind_c22_phy_write,
+               .phy_read_c45 = mt7531_ind_c45_phy_read,
+               .phy_write_c45 = mt7531_ind_c45_phy_write,
+-              .pad_setup = mt7531_pad_setup,
+               .cpu_port_config = mt7531_cpu_port_config,
+               .mac_port_get_caps = mt7531_mac_port_get_caps,
+               .mac_port_config = mt7531_mac_config,
+@@ -3412,7 +3382,6 @@ const struct mt753x_info mt753x_table[]
+               .phy_write_c22 = mt7531_ind_c22_phy_write,
+               .phy_read_c45 = mt7531_ind_c45_phy_read,
+               .phy_write_c45 = mt7531_ind_c45_phy_write,
+-              .pad_setup = mt7988_pad_setup,
+               .cpu_port_config = mt7988_cpu_port_config,
+               .mac_port_get_caps = mt7988_mac_port_get_caps,
+               .mac_port_config = mt7988_mac_config,
+@@ -3442,9 +3411,8 @@ mt7530_probe_common(struct mt7530_priv *
+       /* Sanity check if these required device operations are filled
+        * properly.
+        */
+-      if (!priv->info->sw_setup || !priv->info->pad_setup ||
+-          !priv->info->phy_read_c22 || !priv->info->phy_write_c22 ||
+-          !priv->info->mac_port_get_caps ||
++      if (!priv->info->sw_setup || !priv->info->phy_read_c22 ||
++          !priv->info->phy_write_c22 || !priv->info->mac_port_get_caps ||
+           !priv->info->mac_port_config)
+               return -EINVAL;
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -734,8 +734,6 @@ struct mt753x_pcs {
+  * @phy_write_c22:    Holding the way writing PHY port using C22
+  * @phy_read_c45:     Holding the way reading PHY port using C45
+  * @phy_write_c45:    Holding the way writing PHY port using C45
+- * @pad_setup:                Holding the way setting up the bus pad for a certain
+- *                    MAC port
+  * @phy_mode_supported:       Check if the PHY type is being supported on a certain
+  *                    port
+  * @mac_port_validate:        Holding the way to set addition validate type for a
+@@ -756,7 +754,6 @@ struct mt753x_info {
+                           int regnum);
+       int (*phy_write_c45)(struct mt7530_priv *priv, int port, int devad,
+                            int regnum, u16 val);
+-      int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface);
+       int (*cpu_port_config)(struct dsa_switch *ds, int port);
+       void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
+                                 struct phylink_config *config);
diff --git a/target/linux/generic/backport-6.6/790-17-v6.9-net-dsa-mt7530-correct-port-capabilities-of-MT7988.patch b/target/linux/generic/backport-6.6/790-17-v6.9-net-dsa-mt7530-correct-port-capabilities-of-MT7988.patch
new file mode 100644 (file)
index 0000000..f6c2919
--- /dev/null
@@ -0,0 +1,36 @@
+From 9716e3e2c21547c97a9d79119da8fdce5659c2cc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Tue, 6 Feb 2024 01:08:07 +0300
+Subject: [PATCH 17/30] net: dsa: mt7530: correct port capabilities of MT7988
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+On the switch on the MT7988 SoC, as shown in Block Diagram 8.1.1.3 on page
+125 of "MT7988A Wi-Fi 7 Generation Router Platform: Datasheet (Open
+Version) v0.1", there are only 4 PHYs. That's port 0 to 3. Set the case for
+ports which connect to switch PHYs to '0 ... 3'.
+
+Port 4 and 5 are not used at all in this design.
+
+Link: https://wiki.banana-pi.org/Banana_Pi_BPI-R4#Documents [1]
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-6-d7d92a185cb1@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2790,7 +2790,7 @@ static void mt7988_mac_port_get_caps(str
+       switch (port) {
+       /* Ports which are connected to switch PHYs. There is no MII pinout. */
+-      case 0 ... 4:
++      case 0 ... 3:
+               __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+                         config->supported_interfaces);
+               break;
diff --git a/target/linux/generic/backport-6.6/790-18-v6.9-net-dsa-mt7530-do-not-clear-config-supported_interfa.patch b/target/linux/generic/backport-6.6/790-18-v6.9-net-dsa-mt7530-do-not-clear-config-supported_interfa.patch
new file mode 100644 (file)
index 0000000..66a9158
--- /dev/null
@@ -0,0 +1,38 @@
+From 4d7b17712513710778c0f2f83ea5d9b55ed58c36 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Tue, 6 Feb 2024 01:08:08 +0300
+Subject: [PATCH 18/30] net: dsa: mt7530: do not clear
+ config->supported_interfaces
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There's no need to clear the config->supported_interfaces bitmap before
+reporting the supported interfaces as all bits in the bitmap will already
+be initialized to zero when the phylink_config structure is allocated. The
+"config" pointer points to &dp->phylink_config, and "dp" is allocated by
+dsa_port_touch() with kzalloc(), so all its fields are filled with zeroes.
+
+There's no code that would change the bitmap beforehand. Remove it.
+
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Link: https://lore.kernel.org/r/20240206-for-netnext-mt7530-improvements-2-v5-7-d7d92a185cb1@arinc9.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2786,8 +2786,6 @@ static void mt7531_mac_port_get_caps(str
+ static void mt7988_mac_port_get_caps(struct dsa_switch *ds, int port,
+                                    struct phylink_config *config)
+ {
+-      phy_interface_zero(config->supported_interfaces);
+-
+       switch (port) {
+       /* Ports which are connected to switch PHYs. There is no MII pinout. */
+       case 0 ... 3:
diff --git a/target/linux/generic/backport-6.6/790-19-v6.9-net-dsa-mt7530-remove-.mac_port_config-for-MT7988-an.patch b/target/linux/generic/backport-6.6/790-19-v6.9-net-dsa-mt7530-remove-.mac_port_config-for-MT7988-an.patch
new file mode 100644 (file)
index 0000000..abc1108
--- /dev/null
@@ -0,0 +1,81 @@
+From 69e689e28191f9a242de6821a85f2c5ae4dbd5ae Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Fri, 1 Mar 2024 12:42:57 +0200
+Subject: [PATCH 19/30] net: dsa: mt7530: remove .mac_port_config for MT7988
+ and make it optional
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For the switch on the MT7988 SoC, the mac_port_config member for ID_MT7988
+in mt753x_table is not needed as the interfaces of all MACs are already
+handled on mt7988_mac_port_get_caps().
+
+Therefore, remove the mac_port_config member from ID_MT7988 in
+mt753x_table. Before calling priv->info->mac_port_config(), if there's no
+mac_port_config member in mt753x_table, exit mt753x_mac_config()
+successfully.
+
+Remove calling priv->info->mac_port_config() from the sanity check as the
+sanity check requires a pointer to a mac_port_config function to be
+non-NULL. This will fail for MT7988 as mac_port_config won't be a member of
+its info table.
+
+Co-developed-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 18 ++++--------------
+ 1 file changed, 4 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2869,17 +2869,6 @@ static bool mt753x_is_mac_port(u32 port)
+ }
+ static int
+-mt7988_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+-                phy_interface_t interface)
+-{
+-      if (dsa_is_cpu_port(ds, port) &&
+-          interface == PHY_INTERFACE_MODE_INTERNAL)
+-              return 0;
+-
+-      return -EINVAL;
+-}
+-
+-static int
+ mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+                 phy_interface_t interface)
+ {
+@@ -2919,6 +2908,9 @@ mt753x_mac_config(struct dsa_switch *ds,
+ {
+       struct mt7530_priv *priv = ds->priv;
++      if (!priv->info->mac_port_config)
++              return 0;
++
+       return priv->info->mac_port_config(ds, port, mode, state->interface);
+ }
+@@ -3382,7 +3374,6 @@ const struct mt753x_info mt753x_table[]
+               .phy_write_c45 = mt7531_ind_c45_phy_write,
+               .cpu_port_config = mt7988_cpu_port_config,
+               .mac_port_get_caps = mt7988_mac_port_get_caps,
+-              .mac_port_config = mt7988_mac_config,
+       },
+ };
+ EXPORT_SYMBOL_GPL(mt753x_table);
+@@ -3410,8 +3401,7 @@ mt7530_probe_common(struct mt7530_priv *
+        * properly.
+        */
+       if (!priv->info->sw_setup || !priv->info->phy_read_c22 ||
+-          !priv->info->phy_write_c22 || !priv->info->mac_port_get_caps ||
+-          !priv->info->mac_port_config)
++          !priv->info->phy_write_c22 || !priv->info->mac_port_get_caps)
+               return -EINVAL;
+       priv->id = priv->info->id;
diff --git a/target/linux/generic/backport-6.6/790-20-v6.9-net-dsa-mt7530-set-interrupt-register-only-for-MT753.patch b/target/linux/generic/backport-6.6/790-20-v6.9-net-dsa-mt7530-set-interrupt-register-only-for-MT753.patch
new file mode 100644 (file)
index 0000000..d6eaaaf
--- /dev/null
@@ -0,0 +1,31 @@
+From f8faa3a04ca860b31f22d7d526c5e3f3de511a8f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Fri, 1 Mar 2024 12:42:58 +0200
+Subject: [PATCH 20/30] net: dsa: mt7530: set interrupt register only for
+ MT7530
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Setting this register related to interrupts is only needed for the MT7530
+switch. Make an exclusive check to ensure this.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2254,7 +2254,7 @@ mt7530_setup_irq(struct mt7530_priv *pri
+       }
+       /* This register must be set for MT7530 to properly fire interrupts */
+-      if (priv->id != ID_MT7531)
++      if (priv->id == ID_MT7530 || priv->id == ID_MT7621)
+               mt7530_set(priv, MT7530_TOP_SIG_CTRL, TOP_SIG_CTRL_NORMAL);
+       ret = request_threaded_irq(priv->irq, NULL, mt7530_irq_thread_fn,
diff --git a/target/linux/generic/backport-6.6/790-21-v6.9-net-dsa-mt7530-do-not-use-SW_PHY_RST-to-reset-MT7531.patch b/target/linux/generic/backport-6.6/790-21-v6.9-net-dsa-mt7530-do-not-use-SW_PHY_RST-to-reset-MT7531.patch
new file mode 100644 (file)
index 0000000..735775d
--- /dev/null
@@ -0,0 +1,41 @@
+From 80f4f866d7dad41b12cf37476c38766a89b8b5c4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Fri, 1 Mar 2024 12:42:59 +0200
+Subject: [PATCH 21/30] net: dsa: mt7530: do not use SW_PHY_RST to reset MT7531
+ switch
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+According to the document MT7531 Reference Manual for Development Board
+v1.0, the SW_PHY_RST bit on the SYS_CTRL register doesn't exist for
+MT7531. This is likely why forcing link down on all ports is necessary for
+MT7531.
+
+Therefore, do not set SW_PHY_RST on mt7531_setup().
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2657,14 +2657,12 @@ mt7531_setup(struct dsa_switch *ds)
+       val = mt7530_read(priv, MT7531_TOP_SIG_SR);
+       priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN);
+-      /* all MACs must be forced link-down before sw reset */
++      /* Force link down on all ports before internal reset */
+       for (i = 0; i < MT7530_NUM_PORTS; i++)
+               mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK);
+       /* Reset the switch through internal reset */
+-      mt7530_write(priv, MT7530_SYS_CTRL,
+-                   SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
+-                   SYS_CTRL_REG_RST);
++      mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
+       if (!priv->p5_sgmii) {
+               mt7531_pll_setup(priv);
diff --git a/target/linux/generic/backport-6.6/790-22-v6.9-net-dsa-mt7530-get-rid-of-useless-error-returns-on-p.patch b/target/linux/generic/backport-6.6/790-22-v6.9-net-dsa-mt7530-get-rid-of-useless-error-returns-on-p.patch
new file mode 100644 (file)
index 0000000..c9159a1
--- /dev/null
@@ -0,0 +1,217 @@
+From 58670652cacb7c5752e01f29979d0ca4cdbfcc0a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Fri, 1 Mar 2024 12:43:00 +0200
+Subject: [PATCH 22/30] net: dsa: mt7530: get rid of useless error returns on
+ phylink code path
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Remove error returns on the cases where they are already handled with the
+function the mac_port_get_caps member in mt753x_table points to.
+
+mt7531_mac_config() is also called from mt7531_cpu_port_config() outside of
+phylink but the port and interface modes are already handled there.
+
+Change the functions and the mac_port_config function pointer to void now
+that there're no error returns anymore.
+
+Remove mt753x_is_mac_port() that used to help the said error returns.
+
+On mt7531_mac_config(), switch to if statements to simplify the code.
+
+Remove internal phy cases from mt753x_phylink_mac_config(), there is no
+need to check the interface mode as that's already handled with the
+function the mac_port_get_caps member in mt753x_table points to.
+
+Acked-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 81 ++++++++--------------------------------
+ drivers/net/dsa/mt7530.h |  6 +--
+ 2 files changed, 19 insertions(+), 68 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2800,7 +2800,7 @@ static void mt7988_mac_port_get_caps(str
+       }
+ }
+-static int
++static void
+ mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+                 phy_interface_t interface)
+ {
+@@ -2810,22 +2810,14 @@ mt7530_mac_config(struct dsa_switch *ds,
+               mt7530_setup_port5(priv->ds, interface);
+       else if (port == 6)
+               mt7530_setup_port6(priv->ds, interface);
+-
+-      return 0;
+ }
+-static int mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port,
+-                            phy_interface_t interface,
+-                            struct phy_device *phydev)
++static void mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port,
++                             phy_interface_t interface,
++                             struct phy_device *phydev)
+ {
+       u32 val;
+-      if (priv->p5_sgmii) {
+-              dev_err(priv->dev, "RGMII mode is not available for port %d\n",
+-                      port);
+-              return -EINVAL;
+-      }
+-
+       val = mt7530_read(priv, MT7531_CLKGEN_CTRL);
+       val |= GP_CLK_EN;
+       val &= ~GP_MODE_MASK;
+@@ -2853,20 +2845,14 @@ static int mt7531_rgmii_setup(struct mt7
+               case PHY_INTERFACE_MODE_RGMII_ID:
+                       break;
+               default:
+-                      return -EINVAL;
++                      break;
+               }
+       }
+-      mt7530_write(priv, MT7531_CLKGEN_CTRL, val);
+-      return 0;
+-}
+-
+-static bool mt753x_is_mac_port(u32 port)
+-{
+-      return (port == 5 || port == 6);
++      mt7530_write(priv, MT7531_CLKGEN_CTRL, val);
+ }
+-static int
++static void
+ mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+                 phy_interface_t interface)
+ {
+@@ -2874,42 +2860,21 @@ mt7531_mac_config(struct dsa_switch *ds,
+       struct phy_device *phydev;
+       struct dsa_port *dp;
+-      if (!mt753x_is_mac_port(port)) {
+-              dev_err(priv->dev, "port %d is not a MAC port\n", port);
+-              return -EINVAL;
+-      }
+-
+-      switch (interface) {
+-      case PHY_INTERFACE_MODE_RGMII:
+-      case PHY_INTERFACE_MODE_RGMII_ID:
+-      case PHY_INTERFACE_MODE_RGMII_RXID:
+-      case PHY_INTERFACE_MODE_RGMII_TXID:
++      if (phy_interface_mode_is_rgmii(interface)) {
+               dp = dsa_to_port(ds, port);
+               phydev = dp->slave->phydev;
+-              return mt7531_rgmii_setup(priv, port, interface, phydev);
+-      case PHY_INTERFACE_MODE_SGMII:
+-      case PHY_INTERFACE_MODE_NA:
+-      case PHY_INTERFACE_MODE_1000BASEX:
+-      case PHY_INTERFACE_MODE_2500BASEX:
+-              /* handled in SGMII PCS driver */
+-              return 0;
+-      default:
+-              return -EINVAL;
++              mt7531_rgmii_setup(priv, port, interface, phydev);
+       }
+-
+-      return -EINVAL;
+ }
+-static int
++static void
+ mt753x_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+                 const struct phylink_link_state *state)
+ {
+       struct mt7530_priv *priv = ds->priv;
+-      if (!priv->info->mac_port_config)
+-              return 0;
+-
+-      return priv->info->mac_port_config(ds, port, mode, state->interface);
++      if (priv->info->mac_port_config)
++              priv->info->mac_port_config(ds, port, mode, state->interface);
+ }
+ static struct phylink_pcs *
+@@ -2938,17 +2903,11 @@ mt753x_phylink_mac_config(struct dsa_swi
+       u32 mcr_cur, mcr_new;
+       switch (port) {
+-      case 0 ... 4:
+-              if (state->interface != PHY_INTERFACE_MODE_GMII &&
+-                  state->interface != PHY_INTERFACE_MODE_INTERNAL)
+-                      goto unsupported;
+-              break;
+       case 5:
+               if (priv->p5_interface == state->interface)
+                       break;
+-              if (mt753x_mac_config(ds, port, mode, state) < 0)
+-                      goto unsupported;
++              mt753x_mac_config(ds, port, mode, state);
+               if (priv->p5_intf_sel != P5_DISABLED)
+                       priv->p5_interface = state->interface;
+@@ -2957,16 +2916,10 @@ mt753x_phylink_mac_config(struct dsa_swi
+               if (priv->p6_interface == state->interface)
+                       break;
+-              if (mt753x_mac_config(ds, port, mode, state) < 0)
+-                      goto unsupported;
++              mt753x_mac_config(ds, port, mode, state);
+               priv->p6_interface = state->interface;
+               break;
+-      default:
+-unsupported:
+-              dev_err(ds->dev, "%s: unsupported %s port: %i\n",
+-                      __func__, phy_modes(state->interface), port);
+-              return;
+       }
+       mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
+@@ -3049,7 +3002,6 @@ mt7531_cpu_port_config(struct dsa_switch
+       struct mt7530_priv *priv = ds->priv;
+       phy_interface_t interface;
+       int speed;
+-      int ret;
+       switch (port) {
+       case 5:
+@@ -3074,9 +3026,8 @@ mt7531_cpu_port_config(struct dsa_switch
+       else
+               speed = SPEED_1000;
+-      ret = mt7531_mac_config(ds, port, MLO_AN_FIXED, interface);
+-      if (ret)
+-              return ret;
++      mt7531_mac_config(ds, port, MLO_AN_FIXED, interface);
++
+       mt7530_write(priv, MT7530_PMCR_P(port),
+                    PMCR_CPU_PORT_SETTING(priv->id));
+       mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL,
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -760,9 +760,9 @@ struct mt753x_info {
+       void (*mac_port_validate)(struct dsa_switch *ds, int port,
+                                 phy_interface_t interface,
+                                 unsigned long *supported);
+-      int (*mac_port_config)(struct dsa_switch *ds, int port,
+-                             unsigned int mode,
+-                             phy_interface_t interface);
++      void (*mac_port_config)(struct dsa_switch *ds, int port,
++                              unsigned int mode,
++                              phy_interface_t interface);
+ };
+ /* struct mt7530_priv -       This is the main data structure for holding the state
diff --git a/target/linux/generic/backport-6.6/790-23-v6.9-net-dsa-mt7530-get-rid-of-priv-info-cpu_port_config.patch b/target/linux/generic/backport-6.6/790-23-v6.9-net-dsa-mt7530-get-rid-of-priv-info-cpu_port_config.patch
new file mode 100644 (file)
index 0000000..c52cb0d
--- /dev/null
@@ -0,0 +1,305 @@
+From 859df5cf6ff07a9c930be4681284346aa73dd1fb Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Fri, 1 Mar 2024 12:43:01 +0200
+Subject: [PATCH 23/30] net: dsa: mt7530: get rid of
+ priv->info->cpu_port_config()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+priv->info->cpu_port_config() is used for MT7531 and the switch on the
+MT7988 SoC. It sets up the ports described as a CPU port earlier than the
+phylink code path would do.
+
+This function is useless as:
+- Configuring the MACs can be done from the phylink_mac_config code path
+  instead.
+- All the link configuration it does on the CPU ports are later undone with
+  the port_enable, phylink_mac_config, and then phylink_mac_link_up code
+  path [1].
+
+priv->p5_interface and priv->p6_interface were being used to prevent
+configuring the MACs from the phylink_mac_config code path. Remove them now
+that they hold no purpose.
+
+Remove priv->info->cpu_port_config(). On mt753x_phylink_mac_config, switch
+to if statements to simplify the code.
+
+Remove the overwriting of the speed and duplex interfaces for certain
+interface modes. Phylink already provides the speed and duplex variables
+with proper values. Phylink already sets the max speed of TRGMII to
+SPEED_1000. Add SPEED_2500 for PHY_INTERFACE_MODE_2500BASEX to where the
+speed and EEE bits are set instead.
+
+On the switch on the MT7988 SoC, PHY_INTERFACE_MODE_INTERNAL is being used
+to describe the interface mode of the 10G MAC, which is of port 6. On
+mt7988_cpu_port_config() PMCR_FORCE_SPEED_1000 was set via the
+PMCR_CPU_PORT_SETTING() mask. Add SPEED_10000 case to where the speed bits
+are set to cover this. No need to add it to where the EEE bits are set as
+the "MT7988A Wi-Fi 7 Generation Router Platform: Datasheet (Open Version)
+v0.1" document shows that these bits don't exist on the MT7530_PMCR_P(6)
+register.
+
+Remove the definition of PMCR_CPU_PORT_SETTING() now that it holds no
+purpose.
+
+Change mt753x_cpu_port_enable() to void now that there're no error cases
+left.
+
+Link: https://lore.kernel.org/netdev/ZHy2jQLesdYFMQtO@shell.armlinux.org.uk/ [1]
+Suggested-by: Russell King (Oracle) <linux@armlinux.org.uk>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 114 +++------------------------------------
+ drivers/net/dsa/mt7530.h |  11 ----
+ 2 files changed, 7 insertions(+), 118 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1163,18 +1163,10 @@ mt753x_trap_frames(struct mt7530_priv *p
+                          MT753X_BPDU_CPU_ONLY);
+ }
+-static int
++static void
+ mt753x_cpu_port_enable(struct dsa_switch *ds, int port)
+ {
+       struct mt7530_priv *priv = ds->priv;
+-      int ret;
+-
+-      /* Setup max capability of CPU port at first */
+-      if (priv->info->cpu_port_config) {
+-              ret = priv->info->cpu_port_config(ds, port);
+-              if (ret)
+-                      return ret;
+-      }
+       /* Enable Mediatek header mode on the cpu port */
+       mt7530_write(priv, MT7530_PVC_P(port),
+@@ -1200,8 +1192,6 @@ mt753x_cpu_port_enable(struct dsa_switch
+       /* Set to fallback mode for independent VLAN learning */
+       mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
+                  MT7530_PORT_FALLBACK_MODE);
+-
+-      return 0;
+ }
+ static int
+@@ -2458,8 +2448,6 @@ mt7530_setup(struct dsa_switch *ds)
+       val |= MHWTRAP_MANUAL;
+       mt7530_write(priv, MT7530_MHWTRAP, val);
+-      priv->p6_interface = PHY_INTERFACE_MODE_NA;
+-
+       if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_40MHZ)
+               mt7530_pll_setup(priv);
+@@ -2477,9 +2465,7 @@ mt7530_setup(struct dsa_switch *ds)
+               mt7530_set(priv, MT7530_PSC_P(i), SA_DIS);
+               if (dsa_is_cpu_port(ds, i)) {
+-                      ret = mt753x_cpu_port_enable(ds, i);
+-                      if (ret)
+-                              return ret;
++                      mt753x_cpu_port_enable(ds, i);
+               } else {
+                       mt7530_port_disable(ds, i);
+@@ -2586,9 +2572,7 @@ mt7531_setup_common(struct dsa_switch *d
+               mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR);
+               if (dsa_is_cpu_port(ds, i)) {
+-                      ret = mt753x_cpu_port_enable(ds, i);
+-                      if (ret)
+-                              return ret;
++                      mt753x_cpu_port_enable(ds, i);
+               } else {
+                       mt7530_port_disable(ds, i);
+@@ -2680,10 +2664,6 @@ mt7531_setup(struct dsa_switch *ds)
+       mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK,
+                  MT7531_GPIO0_INTERRUPT);
+-      /* Let phylink decide the interface later. */
+-      priv->p5_interface = PHY_INTERFACE_MODE_NA;
+-      priv->p6_interface = PHY_INTERFACE_MODE_NA;
+-
+       /* Enable Energy-Efficient Ethernet (EEE) and PHY core PLL, since
+        * phy_device has not yet been created provided for
+        * phy_[read,write]_mmd_indirect is called, we provide our own
+@@ -2902,26 +2882,9 @@ mt753x_phylink_mac_config(struct dsa_swi
+       struct mt7530_priv *priv = ds->priv;
+       u32 mcr_cur, mcr_new;
+-      switch (port) {
+-      case 5:
+-              if (priv->p5_interface == state->interface)
+-                      break;
+-
++      if (port == 5 || port == 6)
+               mt753x_mac_config(ds, port, mode, state);
+-              if (priv->p5_intf_sel != P5_DISABLED)
+-                      priv->p5_interface = state->interface;
+-              break;
+-      case 6:
+-              if (priv->p6_interface == state->interface)
+-                      break;
+-
+-              mt753x_mac_config(ds, port, mode, state);
+-
+-              priv->p6_interface = state->interface;
+-              break;
+-      }
+-
+       mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
+       mcr_new = mcr_cur;
+       mcr_new &= ~PMCR_LINK_SETTINGS_MASK;
+@@ -2957,17 +2920,10 @@ static void mt753x_phylink_mac_link_up(s
+       mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK;
+-      /* MT753x MAC works in 1G full duplex mode for all up-clocked
+-       * variants.
+-       */
+-      if (interface == PHY_INTERFACE_MODE_TRGMII ||
+-          (phy_interface_mode_is_8023z(interface))) {
+-              speed = SPEED_1000;
+-              duplex = DUPLEX_FULL;
+-      }
+-
+       switch (speed) {
+       case SPEED_1000:
++      case SPEED_2500:
++      case SPEED_10000:
+               mcr |= PMCR_FORCE_SPEED_1000;
+               break;
+       case SPEED_100:
+@@ -2985,6 +2941,7 @@ static void mt753x_phylink_mac_link_up(s
+       if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) {
+               switch (speed) {
+               case SPEED_1000:
++              case SPEED_2500:
+                       mcr |= PMCR_FORCE_EEE1G;
+                       break;
+               case SPEED_100:
+@@ -2996,61 +2953,6 @@ static void mt753x_phylink_mac_link_up(s
+       mt7530_set(priv, MT7530_PMCR_P(port), mcr);
+ }
+-static int
+-mt7531_cpu_port_config(struct dsa_switch *ds, int port)
+-{
+-      struct mt7530_priv *priv = ds->priv;
+-      phy_interface_t interface;
+-      int speed;
+-
+-      switch (port) {
+-      case 5:
+-              if (!priv->p5_sgmii)
+-                      interface = PHY_INTERFACE_MODE_RGMII;
+-              else
+-                      interface = PHY_INTERFACE_MODE_2500BASEX;
+-
+-              priv->p5_interface = interface;
+-              break;
+-      case 6:
+-              interface = PHY_INTERFACE_MODE_2500BASEX;
+-
+-              priv->p6_interface = interface;
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      if (interface == PHY_INTERFACE_MODE_2500BASEX)
+-              speed = SPEED_2500;
+-      else
+-              speed = SPEED_1000;
+-
+-      mt7531_mac_config(ds, port, MLO_AN_FIXED, interface);
+-
+-      mt7530_write(priv, MT7530_PMCR_P(port),
+-                   PMCR_CPU_PORT_SETTING(priv->id));
+-      mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL,
+-                                 speed, DUPLEX_FULL, true, true);
+-
+-      return 0;
+-}
+-
+-static int
+-mt7988_cpu_port_config(struct dsa_switch *ds, int port)
+-{
+-      struct mt7530_priv *priv = ds->priv;
+-
+-      mt7530_write(priv, MT7530_PMCR_P(port),
+-                   PMCR_CPU_PORT_SETTING(priv->id));
+-
+-      mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED,
+-                                 PHY_INTERFACE_MODE_INTERNAL, NULL,
+-                                 SPEED_10000, DUPLEX_FULL, true, true);
+-
+-      return 0;
+-}
+-
+ static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
+                                   struct phylink_config *config)
+ {
+@@ -3309,7 +3211,6 @@ const struct mt753x_info mt753x_table[]
+               .phy_write_c22 = mt7531_ind_c22_phy_write,
+               .phy_read_c45 = mt7531_ind_c45_phy_read,
+               .phy_write_c45 = mt7531_ind_c45_phy_write,
+-              .cpu_port_config = mt7531_cpu_port_config,
+               .mac_port_get_caps = mt7531_mac_port_get_caps,
+               .mac_port_config = mt7531_mac_config,
+       },
+@@ -3321,7 +3222,6 @@ const struct mt753x_info mt753x_table[]
+               .phy_write_c22 = mt7531_ind_c22_phy_write,
+               .phy_read_c45 = mt7531_ind_c45_phy_read,
+               .phy_write_c45 = mt7531_ind_c45_phy_write,
+-              .cpu_port_config = mt7988_cpu_port_config,
+               .mac_port_get_caps = mt7988_mac_port_get_caps,
+       },
+ };
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -340,13 +340,6 @@ enum mt7530_vlan_port_acc_frm {
+                                        PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
+                                        PMCR_FORCE_FDX | PMCR_FORCE_LNK | \
+                                        PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100)
+-#define  PMCR_CPU_PORT_SETTING(id)    (PMCR_FORCE_MODE_ID((id)) | \
+-                                       PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
+-                                       PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \
+-                                       PMCR_TX_EN | PMCR_RX_EN | \
+-                                       PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
+-                                       PMCR_FORCE_SPEED_1000 | \
+-                                       PMCR_FORCE_FDX | PMCR_FORCE_LNK)
+ #define MT7530_PMEEECR_P(x)           (0x3004 + (x) * 0x100)
+ #define  WAKEUP_TIME_1000(x)          (((x) & 0xFF) << 24)
+@@ -754,7 +747,6 @@ struct mt753x_info {
+                           int regnum);
+       int (*phy_write_c45)(struct mt7530_priv *priv, int port, int devad,
+                            int regnum, u16 val);
+-      int (*cpu_port_config)(struct dsa_switch *ds, int port);
+       void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
+                                 struct phylink_config *config);
+       void (*mac_port_validate)(struct dsa_switch *ds, int port,
+@@ -780,7 +772,6 @@ struct mt753x_info {
+  * @ports:            Holding the state among ports
+  * @reg_mutex:                The lock for protecting among process accessing
+  *                    registers
+- * @p6_interface      Holding the current port 6 interface
+  * @p5_intf_sel:      Holding the current port 5 interface select
+  * @p5_sgmii:         Flag for distinguishing if port 5 of the MT7531 switch
+  *                    has got SGMII
+@@ -802,8 +793,6 @@ struct mt7530_priv {
+       const struct mt753x_info *info;
+       unsigned int            id;
+       bool                    mcm;
+-      phy_interface_t         p6_interface;
+-      phy_interface_t         p5_interface;
+       enum p5_interface_select p5_intf_sel;
+       bool                    p5_sgmii;
+       u8                      mirror_rx;
diff --git a/target/linux/generic/backport-6.6/790-24-v6.9-net-dsa-mt7530-get-rid-of-mt753x_mac_config.patch b/target/linux/generic/backport-6.6/790-24-v6.9-net-dsa-mt7530-get-rid-of-mt753x_mac_config.patch
new file mode 100644 (file)
index 0000000..7fe77c5
--- /dev/null
@@ -0,0 +1,48 @@
+From c74a98baa8d098157975b3f94e496dd3a73e0864 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Fri, 1 Mar 2024 12:43:02 +0200
+Subject: [PATCH 24/30] net: dsa: mt7530: get rid of mt753x_mac_config()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There is no need for a separate function to call
+priv->info->mac_port_config(). Call it from mt753x_phylink_mac_config()
+instead and remove mt753x_mac_config().
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 14 ++------------
+ 1 file changed, 2 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2847,16 +2847,6 @@ mt7531_mac_config(struct dsa_switch *ds,
+       }
+ }
+-static void
+-mt753x_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+-                const struct phylink_link_state *state)
+-{
+-      struct mt7530_priv *priv = ds->priv;
+-
+-      if (priv->info->mac_port_config)
+-              priv->info->mac_port_config(ds, port, mode, state->interface);
+-}
+-
+ static struct phylink_pcs *
+ mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
+                             phy_interface_t interface)
+@@ -2882,8 +2872,8 @@ mt753x_phylink_mac_config(struct dsa_swi
+       struct mt7530_priv *priv = ds->priv;
+       u32 mcr_cur, mcr_new;
+-      if (port == 5 || port == 6)
+-              mt753x_mac_config(ds, port, mode, state);
++      if ((port == 5 || port == 6) && priv->info->mac_port_config)
++              priv->info->mac_port_config(ds, port, mode, state->interface);
+       mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
+       mcr_new = mcr_cur;
diff --git a/target/linux/generic/backport-6.6/790-25-v6.9-net-dsa-mt7530-put-initialising-PCS-devices-code-bac.patch b/target/linux/generic/backport-6.6/790-25-v6.9-net-dsa-mt7530-put-initialising-PCS-devices-code-bac.patch
new file mode 100644 (file)
index 0000000..bd5c9b9
--- /dev/null
@@ -0,0 +1,57 @@
+From ab1ddb241bc1cb3d80aa51207810edd5cb0bbdc5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Fri, 1 Mar 2024 12:43:03 +0200
+Subject: [PATCH 25/30] net: dsa: mt7530: put initialising PCS devices code
+ back to original order
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The commit fae463084032 ("net: dsa: mt753x: fix pcs conversion regression")
+fixes regression caused by cpu_port_config manually calling phylink
+operations. cpu_port_config was deemed useless and was removed. Therefore,
+put initialising PCS devices code back to its original order.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -3025,17 +3025,9 @@ static int
+ mt753x_setup(struct dsa_switch *ds)
+ {
+       struct mt7530_priv *priv = ds->priv;
+-      int i, ret;
++      int ret = priv->info->sw_setup(ds);
++      int i;
+-      /* Initialise the PCS devices */
+-      for (i = 0; i < priv->ds->num_ports; i++) {
+-              priv->pcs[i].pcs.ops = priv->info->pcs_ops;
+-              priv->pcs[i].pcs.neg_mode = true;
+-              priv->pcs[i].priv = priv;
+-              priv->pcs[i].port = i;
+-      }
+-
+-      ret = priv->info->sw_setup(ds);
+       if (ret)
+               return ret;
+@@ -3047,6 +3039,14 @@ mt753x_setup(struct dsa_switch *ds)
+       if (ret && priv->irq)
+               mt7530_free_irq_common(priv);
++      /* Initialise the PCS devices */
++      for (i = 0; i < priv->ds->num_ports; i++) {
++              priv->pcs[i].pcs.ops = priv->info->pcs_ops;
++              priv->pcs[i].pcs.neg_mode = true;
++              priv->pcs[i].priv = priv;
++              priv->pcs[i].port = i;
++      }
++
+       if (priv->create_sgmii) {
+               ret = priv->create_sgmii(priv);
+               if (ret && priv->irq)
diff --git a/target/linux/generic/backport-6.6/790-26-v6.9-net-dsa-mt7530-sort-link-settings-ops-and-force-link.patch b/target/linux/generic/backport-6.6/790-26-v6.9-net-dsa-mt7530-sort-link-settings-ops-and-force-link.patch
new file mode 100644 (file)
index 0000000..348c35e
--- /dev/null
@@ -0,0 +1,68 @@
+From aa474698f75f4790a4de2052dd487736d2361b2e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Fri, 1 Mar 2024 12:43:04 +0200
+Subject: [PATCH 26/30] net: dsa: mt7530: sort link settings ops and force link
+ down on all ports
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+port_enable and port_disable clears the link settings. Move that to
+mt7530_setup() and mt7531_setup_common() which set up the switches. This
+way, the link settings are cleared on all ports at setup, and then only
+once with phylink_mac_link_down() when a link goes down.
+
+Enable force mode at setup to apply the force part of the link settings.
+This ensures that disabled ports will have their link down.
+
+Suggested-by: Vladimir Oltean <olteanv@gmail.com>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 14 ++++++++++++--
+ 1 file changed, 12 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1215,7 +1215,6 @@ mt7530_port_enable(struct dsa_switch *ds
+       priv->ports[port].enable = true;
+       mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+                  priv->ports[port].pm);
+-      mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
+       mutex_unlock(&priv->reg_mutex);
+@@ -1235,7 +1234,6 @@ mt7530_port_disable(struct dsa_switch *d
+       priv->ports[port].enable = false;
+       mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+                  PCR_MATRIX_CLR);
+-      mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
+       mutex_unlock(&priv->reg_mutex);
+ }
+@@ -2457,6 +2455,12 @@ mt7530_setup(struct dsa_switch *ds)
+       mt7530_mib_reset(ds);
+       for (i = 0; i < MT7530_NUM_PORTS; i++) {
++              /* Clear link settings and enable force mode to force link down
++               * on all ports until they're enabled later.
++               */
++              mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
++                         PMCR_FORCE_MODE, PMCR_FORCE_MODE);
++
+               /* Disable forwarding by default on all ports */
+               mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
+                          PCR_MATRIX_CLR);
+@@ -2562,6 +2566,12 @@ mt7531_setup_common(struct dsa_switch *d
+                    UNU_FFP_MASK);
+       for (i = 0; i < MT7530_NUM_PORTS; i++) {
++              /* Clear link settings and enable force mode to force link down
++               * on all ports until they're enabled later.
++               */
++              mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
++                         MT7531_FORCE_MODE, MT7531_FORCE_MODE);
++
+               /* Disable forwarding by default on all ports */
+               mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
+                          PCR_MATRIX_CLR);
diff --git a/target/linux/generic/backport-6.6/790-27-v6.9-net-dsa-mt7530-simplify-link-operations.patch b/target/linux/generic/backport-6.6/790-27-v6.9-net-dsa-mt7530-simplify-link-operations.patch
new file mode 100644 (file)
index 0000000..54f5a59
--- /dev/null
@@ -0,0 +1,83 @@
+From 1ca89c2e349d7c5e045911d741dacf4c83d029e7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Fri, 1 Mar 2024 12:43:05 +0200
+Subject: [PATCH 27/30] net: dsa: mt7530: simplify link operations
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The "MT7621 Giga Switch Programming Guide v0.3", "MT7531 Reference Manual
+for Development Board v1.0", and "MT7988A Wi-Fi 7 Generation Router
+Platform: Datasheet (Open Version) v0.1" documents show that these bits are
+enabled at reset:
+
+PMCR_IFG_XMIT(1) (not part of PMCR_LINK_SETTINGS_MASK)
+PMCR_MAC_MODE (not part of PMCR_LINK_SETTINGS_MASK)
+PMCR_TX_EN
+PMCR_RX_EN
+PMCR_BACKOFF_EN (not part of PMCR_LINK_SETTINGS_MASK)
+PMCR_BACKPR_EN (not part of PMCR_LINK_SETTINGS_MASK)
+PMCR_TX_FC_EN
+PMCR_RX_FC_EN
+
+These bits also don't exist on the MT7530_PMCR_P(6) register of the switch
+on the MT7988 SoC:
+
+PMCR_IFG_XMIT()
+PMCR_MAC_MODE
+PMCR_BACKOFF_EN
+PMCR_BACKPR_EN
+
+Remove the setting of the bits not part of PMCR_LINK_SETTINGS_MASK on
+phylink_mac_config as they're already set.
+
+The bit for setting the port on force mode is already done on
+mt7530_setup() and mt7531_setup_common(). So get rid of
+PMCR_FORCE_MODE_ID() which helped determine which bit to use for the switch
+model.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 12 +-----------
+ drivers/net/dsa/mt7530.h |  2 --
+ 2 files changed, 1 insertion(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2880,23 +2880,13 @@ mt753x_phylink_mac_config(struct dsa_swi
+                         const struct phylink_link_state *state)
+ {
+       struct mt7530_priv *priv = ds->priv;
+-      u32 mcr_cur, mcr_new;
+       if ((port == 5 || port == 6) && priv->info->mac_port_config)
+               priv->info->mac_port_config(ds, port, mode, state->interface);
+-      mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
+-      mcr_new = mcr_cur;
+-      mcr_new &= ~PMCR_LINK_SETTINGS_MASK;
+-      mcr_new |= PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | PMCR_BACKOFF_EN |
+-                 PMCR_BACKPR_EN | PMCR_FORCE_MODE_ID(priv->id);
+-
+       /* Are we connected to external phy */
+       if (port == 5 && dsa_is_user_port(ds, 5))
+-              mcr_new |= PMCR_EXT_PHY;
+-
+-      if (mcr_new != mcr_cur)
+-              mt7530_write(priv, MT7530_PMCR_P(port), mcr_new);
++              mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY);
+ }
+ static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port,
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -333,8 +333,6 @@ enum mt7530_vlan_port_acc_frm {
+                                        MT7531_FORCE_DPX | \
+                                        MT7531_FORCE_RX_FC | \
+                                        MT7531_FORCE_TX_FC)
+-#define  PMCR_FORCE_MODE_ID(id)               ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
+-                                       MT7531_FORCE_MODE : PMCR_FORCE_MODE)
+ #define  PMCR_LINK_SETTINGS_MASK      (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
+                                        PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
+                                        PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
diff --git a/target/linux/generic/backport-6.6/790-28-v6.9-net-dsa-mt7530-disable-LEDs-before-reset.patch b/target/linux/generic/backport-6.6/790-28-v6.9-net-dsa-mt7530-disable-LEDs-before-reset.patch
new file mode 100644 (file)
index 0000000..af39929
--- /dev/null
@@ -0,0 +1,94 @@
+From de16cf680331cd0bd7db97c3f8d376f5eac39cae Mon Sep 17 00:00:00 2001
+From: Justin Swartz <justin.swartz@risingedge.co.za>
+Date: Tue, 5 Mar 2024 06:39:51 +0200
+Subject: [PATCH 28/30] net: dsa: mt7530: disable LEDs before reset
+
+Disable LEDs just before resetting the MT7530 to avoid
+situations where the ESW_P4_LED_0 and ESW_P3_LED_0 pin
+states may cause an unintended external crystal frequency
+to be selected.
+
+The HT_XTAL_FSEL (External Crystal Frequency Selection)
+field of HWTRAP (the Hardware Trap register) stores a
+2-bit value that represents the state of the ESW_P4_LED_0
+and ESW_P4_LED_0 pins (seemingly) sampled just after the
+MT7530 has been reset, as:
+
+    ESW_P4_LED_0    ESW_P3_LED_0    Frequency
+    -----------------------------------------
+    0               1               20MHz
+    1               0               40MHz
+    1               1               25MHz
+
+The value of HT_XTAL_FSEL is bootstrapped by pulling
+ESW_P4_LED_0 and ESW_P3_LED_0 up or down accordingly,
+but:
+
+  if a 40MHz crystal has been selected and
+  the ESW_P3_LED_0 pin is high during reset,
+
+  or a 20MHz crystal has been selected and
+  the ESW_P4_LED_0 pin is high during reset,
+
+  then the value of HT_XTAL_FSEL will indicate
+  that a 25MHz crystal is present.
+
+By default, the state of the LED pins is PHY controlled
+to reflect the link state.
+
+To illustrate, if a board has:
+
+  5 ports with active low LED control,
+  and HT_XTAL_FSEL bootstrapped for 40MHz.
+
+When the MT7530 is powered up without any external
+connection, only the LED associated with Port 3 is
+illuminated as ESW_P3_LED_0 is low.
+
+In this state, directly after mt7530_setup()'s reset
+is performed, the HWTRAP register (0x7800) reflects
+the intended HT_XTAL_FSEL (HWTRAP bits 10:9) of 40MHz:
+
+  mt7530-mdio mdio-bus:1f: mt7530_read: 00007800 == 00007dcf
+
+  >>> bin(0x7dcf >> 9 & 0b11)
+  '0b10'
+
+But if a cable is connected to Port 3 and the link
+is active before mt7530_setup()'s reset takes place,
+then HT_XTAL_FSEL seems to be set for 25MHz:
+
+  mt7530-mdio mdio-bus:1f: mt7530_read: 00007800 == 00007fcf
+
+  >>> bin(0x7fcf >> 9 & 0b11)
+  '0b11'
+
+Once HT_XTAL_FSEL reflects 25MHz, none of the ports
+are functional until the MT7621 (or MT7530 itself)
+is reset.
+
+By disabling the LED pins just before reset, the chance
+of an unintended HT_XTAL_FSEL value is reduced.
+
+Signed-off-by: Justin Swartz <justin.swartz@risingedge.co.za>
+Link: https://lore.kernel.org/r/20240305043952.21590-1-justin.swartz@risingedge.co.za
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/dsa/mt7530.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2391,6 +2391,12 @@ mt7530_setup(struct dsa_switch *ds)
+               }
+       }
++      /* Disable LEDs before reset to prevent the MT7530 sampling a
++       * potentially incorrect HT_XTAL_FSEL value.
++       */
++      mt7530_write(priv, MT7530_LED_EN, 0);
++      usleep_range(1000, 1100);
++
+       /* Reset whole chip through gpio pin or memory-mapped registers for
+        * different type of hardware
+        */
diff --git a/target/linux/generic/backport-6.6/790-30-v6.9-net-dsa-mt7530-prevent-possible-incorrect-XTAL-frequ.patch b/target/linux/generic/backport-6.6/790-30-v6.9-net-dsa-mt7530-prevent-possible-incorrect-XTAL-frequ.patch
new file mode 100644 (file)
index 0000000..c2eb3a2
--- /dev/null
@@ -0,0 +1,154 @@
+From b9547109205c5e0a27e5bed568b0fc183fff906b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Thu, 14 Mar 2024 12:28:35 +0300
+Subject: [PATCH 30/30] net: dsa: mt7530: prevent possible incorrect XTAL
+ frequency selection
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+On MT7530, the HT_XTAL_FSEL field of the HWTRAP register stores a 2-bit
+value that represents the frequency of the crystal oscillator connected to
+the switch IC. The field is populated by the state of the ESW_P4_LED_0 and
+ESW_P4_LED_0 pins, which is done right after reset is deasserted.
+
+  ESW_P4_LED_0    ESW_P3_LED_0    Frequency
+  -----------------------------------------
+  0               0               Reserved
+  0               1               20MHz
+  1               0               40MHz
+  1               1               25MHz
+
+On MT7531, the XTAL25 bit of the STRAP register stores this. The LAN0LED0
+pin is used to populate the bit. 25MHz when the pin is high, 40MHz when
+it's low.
+
+These pins are also used with LEDs, therefore, their state can be set to
+something other than the bootstrapping configuration. For example, a link
+may be established on port 3 before the DSA subdriver takes control of the
+switch which would set ESW_P3_LED_0 to high.
+
+Currently on mt7530_setup() and mt7531_setup(), 1000 - 1100 usec delay is
+described between reset assertion and deassertion. Some switch ICs in real
+life conditions cannot always have these pins set back to the bootstrapping
+configuration before reset deassertion in this amount of delay. This causes
+wrong crystal frequency to be selected which puts the switch in a
+nonfunctional state after reset deassertion.
+
+The tests below are conducted on an MT7530 with a 40MHz crystal oscillator
+by Justin Swartz.
+
+With a cable from an active peer connected to port 3 before reset, an
+incorrect crystal frequency (0b11 = 25MHz) is selected:
+
+                      [1]                  [3]     [5]
+                      :                    :       :
+              _____________________________         __________________
+ESW_P4_LED_0                               |_______|
+              _____________________________
+ESW_P3_LED_0                               |__________________________
+
+                       :                  : :     :
+                       :                  : [4]...:
+                       :                  :
+                       [2]................:
+
+[1] Reset is asserted.
+[2] Period of 1000 - 1100 usec.
+[3] Reset is deasserted.
+[4] Period of 315 usec. HWTRAP register is populated with incorrect
+    XTAL frequency.
+[5] Signals reflect the bootstrapped configuration.
+
+Increase the delay between reset_control_assert() and
+reset_control_deassert(), and gpiod_set_value_cansleep(priv->reset, 0) and
+gpiod_set_value_cansleep(priv->reset, 1) to 5000 - 5100 usec. This amount
+ensures a higher possibility that the switch IC will have these pins back
+to the bootstrapping configuration before reset deassertion.
+
+With a cable from an active peer connected to port 3 before reset, the
+correct crystal frequency (0b10 = 40MHz) is selected:
+
+                      [1]        [2-1]     [3]     [5]
+                      :          :         :       :
+              _____________________________         __________________
+ESW_P4_LED_0                               |_______|
+              ___________________           _______
+ESW_P3_LED_0                     |_________|       |__________________
+
+                       :          :       : :     :
+                       :          [2-2]...: [4]...:
+                       [2]................:
+
+[1] Reset is asserted.
+[2] Period of 5000 - 5100 usec.
+[2-1] ESW_P3_LED_0 goes low.
+[2-2] Remaining period of 5000 - 5100 usec.
+[3] Reset is deasserted.
+[4] Period of 310 usec. HWTRAP register is populated with bootstrapped
+    XTAL frequency.
+[5] Signals reflect the bootstrapped configuration.
+
+ESW_P3_LED_0 low period before reset deassertion:
+
+              5000 usec
+            - 5100 usec
+    TEST     RESET HOLD
+       #         (usec)
+  ---------------------
+       1           5410
+       2           5440
+       3           4375
+       4           5490
+       5           5475
+       6           4335
+       7           4370
+       8           5435
+       9           4205
+      10           4335
+      11           3750
+      12           3170
+      13           4395
+      14           4375
+      15           3515
+      16           4335
+      17           4220
+      18           4175
+      19           4175
+      20           4350
+
+     Min           3170
+     Max           5490
+
+  Median       4342.500
+     Avg       4466.500
+
+Revert commit 2920dd92b980 ("net: dsa: mt7530: disable LEDs before reset").
+Changing the state of pins via reset assertion is simpler and more
+efficient than doing so by setting the LED controller off.
+
+Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch")
+Fixes: c288575f7810 ("net: dsa: mt7530: Add the support of MT7531 switch")
+Co-developed-by: Justin Swartz <justin.swartz@risingedge.co.za>
+Signed-off-by: Justin Swartz <justin.swartz@risingedge.co.za>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 6 ------
+ 1 file changed, 6 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2391,12 +2391,6 @@ mt7530_setup(struct dsa_switch *ds)
+               }
+       }
+-      /* Disable LEDs before reset to prevent the MT7530 sampling a
+-       * potentially incorrect HT_XTAL_FSEL value.
+-       */
+-      mt7530_write(priv, MT7530_LED_EN, 0);
+-      usleep_range(1000, 1100);
+-
+       /* Reset whole chip through gpio pin or memory-mapped registers for
+        * different type of hardware
+        */
diff --git a/target/linux/generic/backport-6.6/790-33-v6.10-net-dsa-mt7530-provide-own-phylink-MAC-operations.patch b/target/linux/generic/backport-6.6/790-33-v6.10-net-dsa-mt7530-provide-own-phylink-MAC-operations.patch
new file mode 100644 (file)
index 0000000..5eade73
--- /dev/null
@@ -0,0 +1,135 @@
+From 5754b3bdcd872aa229881b8f07f84a8404c7d72a Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 12 Apr 2024 16:15:34 +0100
+Subject: [PATCH 1/5] net: dsa: mt7530: provide own phylink MAC operations
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Convert mt753x to provide its own phylink MAC operations, thus avoiding
+the shim layer in DSA's port.c
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Link: https://lore.kernel.org/r/E1rvIco-006bQu-Fq@rmk-PC.armlinux.org.uk
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 46 +++++++++++++++++++++++++---------------
+ 1 file changed, 29 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2858,28 +2858,34 @@ mt7531_mac_config(struct dsa_switch *ds,
+ }
+ static struct phylink_pcs *
+-mt753x_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
++mt753x_phylink_mac_select_pcs(struct phylink_config *config,
+                             phy_interface_t interface)
+ {
+-      struct mt7530_priv *priv = ds->priv;
++      struct dsa_port *dp = dsa_phylink_to_port(config);
++      struct mt7530_priv *priv = dp->ds->priv;
+       switch (interface) {
+       case PHY_INTERFACE_MODE_TRGMII:
+-              return &priv->pcs[port].pcs;
++              return &priv->pcs[dp->index].pcs;
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_2500BASEX:
+-              return priv->ports[port].sgmii_pcs;
++              return priv->ports[dp->index].sgmii_pcs;
+       default:
+               return NULL;
+       }
+ }
+ static void
+-mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
++mt753x_phylink_mac_config(struct phylink_config *config, unsigned int mode,
+                         const struct phylink_link_state *state)
+ {
+-      struct mt7530_priv *priv = ds->priv;
++      struct dsa_port *dp = dsa_phylink_to_port(config);
++      struct dsa_switch *ds = dp->ds;
++      struct mt7530_priv *priv;
++      int port = dp->index;
++
++      priv = ds->priv;
+       if ((port == 5 || port == 6) && priv->info->mac_port_config)
+               priv->info->mac_port_config(ds, port, mode, state->interface);
+@@ -2889,23 +2895,25 @@ mt753x_phylink_mac_config(struct dsa_swi
+               mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY);
+ }
+-static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port,
++static void mt753x_phylink_mac_link_down(struct phylink_config *config,
+                                        unsigned int mode,
+                                        phy_interface_t interface)
+ {
+-      struct mt7530_priv *priv = ds->priv;
++      struct dsa_port *dp = dsa_phylink_to_port(config);
++      struct mt7530_priv *priv = dp->ds->priv;
+-      mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
++      mt7530_clear(priv, MT7530_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK);
+ }
+-static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
++static void mt753x_phylink_mac_link_up(struct phylink_config *config,
++                                     struct phy_device *phydev,
+                                      unsigned int mode,
+                                      phy_interface_t interface,
+-                                     struct phy_device *phydev,
+                                      int speed, int duplex,
+                                      bool tx_pause, bool rx_pause)
+ {
+-      struct mt7530_priv *priv = ds->priv;
++      struct dsa_port *dp = dsa_phylink_to_port(config);
++      struct mt7530_priv *priv = dp->ds->priv;
+       u32 mcr;
+       mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK;
+@@ -2940,7 +2948,7 @@ static void mt753x_phylink_mac_link_up(s
+               }
+       }
+-      mt7530_set(priv, MT7530_PMCR_P(port), mcr);
++      mt7530_set(priv, MT7530_PMCR_P(dp->index), mcr);
+ }
+ static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
+@@ -3160,16 +3168,19 @@ const struct dsa_switch_ops mt7530_switc
+       .port_mirror_add        = mt753x_port_mirror_add,
+       .port_mirror_del        = mt753x_port_mirror_del,
+       .phylink_get_caps       = mt753x_phylink_get_caps,
+-      .phylink_mac_select_pcs = mt753x_phylink_mac_select_pcs,
+-      .phylink_mac_config     = mt753x_phylink_mac_config,
+-      .phylink_mac_link_down  = mt753x_phylink_mac_link_down,
+-      .phylink_mac_link_up    = mt753x_phylink_mac_link_up,
+       .get_mac_eee            = mt753x_get_mac_eee,
+       .set_mac_eee            = mt753x_set_mac_eee,
+       .master_state_change    = mt753x_conduit_state_change,
+ };
+ EXPORT_SYMBOL_GPL(mt7530_switch_ops);
++static const struct phylink_mac_ops mt753x_phylink_mac_ops = {
++      .mac_select_pcs = mt753x_phylink_mac_select_pcs,
++      .mac_config     = mt753x_phylink_mac_config,
++      .mac_link_down  = mt753x_phylink_mac_link_down,
++      .mac_link_up    = mt753x_phylink_mac_link_up,
++};
++
+ const struct mt753x_info mt753x_table[] = {
+       [ID_MT7621] = {
+               .id = ID_MT7621,
+@@ -3247,6 +3258,7 @@ mt7530_probe_common(struct mt7530_priv *
+       priv->dev = dev;
+       priv->ds->priv = priv;
+       priv->ds->ops = &mt7530_switch_ops;
++      priv->ds->phylink_mac_ops = &mt753x_phylink_mac_ops;
+       mutex_init(&priv->reg_mutex);
+       dev_set_drvdata(dev, priv);
diff --git a/target/linux/generic/backport-6.6/790-36-v6.10-net-dsa-mt7530-mdio-read-PHY-address-of-switch-from-.patch b/target/linux/generic/backport-6.6/790-36-v6.10-net-dsa-mt7530-mdio-read-PHY-address-of-switch-from-.patch
new file mode 100644 (file)
index 0000000..a6cbb0f
--- /dev/null
@@ -0,0 +1,238 @@
+From 5053a6cf1d50d785078562470d2a63695a9f3bf2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Thu, 18 Apr 2024 08:35:30 +0300
+Subject: [PATCH 4/5] net: dsa: mt7530-mdio: read PHY address of switch from
+ device tree
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Read the PHY address the switch listens on from the reg property of the
+switch node on the device tree. This change brings support for MT7530
+switches on boards with such bootstrapping configuration where the switch
+listens on a different PHY address than the hardcoded PHY address on the
+driver, 31.
+
+As described on the "MT7621 Programming Guide v0.4" document, the MT7530
+switch and its PHYs can be configured to listen on the range of 7-12,
+15-20, 23-28, and 31 and 0-4 PHY addresses.
+
+There are operations where the switch PHY registers are used. For the PHY
+address of the control PHY, transform the MT753X_CTRL_PHY_ADDR constant
+into a macro and use it. The PHY address for the control PHY is 0 when the
+switch listens on 31. In any other case, it is one greater than the PHY
+address the switch listens on.
+
+Reviewed-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530-mdio.c | 28 +++++++++++++-------------
+ drivers/net/dsa/mt7530.c      | 37 +++++++++++++++++++++++------------
+ drivers/net/dsa/mt7530.h      |  4 +++-
+ 3 files changed, 41 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/dsa/mt7530-mdio.c
++++ b/drivers/net/dsa/mt7530-mdio.c
+@@ -18,7 +18,8 @@
+ static int
+ mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
+ {
+-      struct mii_bus *bus = context;
++      struct mt7530_priv *priv = context;
++      struct mii_bus *bus = priv->bus;
+       u16 page, r, lo, hi;
+       int ret;
+@@ -27,36 +28,35 @@ mt7530_regmap_write(void *context, unsig
+       lo = val & 0xffff;
+       hi = val >> 16;
+-      /* MT7530 uses 31 as the pseudo port */
+-      ret = bus->write(bus, 0x1f, 0x1f, page);
++      ret = bus->write(bus, priv->mdiodev->addr, 0x1f, page);
+       if (ret < 0)
+               return ret;
+-      ret = bus->write(bus, 0x1f, r,  lo);
++      ret = bus->write(bus, priv->mdiodev->addr, r, lo);
+       if (ret < 0)
+               return ret;
+-      ret = bus->write(bus, 0x1f, 0x10, hi);
++      ret = bus->write(bus, priv->mdiodev->addr, 0x10, hi);
+       return ret;
+ }
+ static int
+ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
+ {
+-      struct mii_bus *bus = context;
++      struct mt7530_priv *priv = context;
++      struct mii_bus *bus = priv->bus;
+       u16 page, r, lo, hi;
+       int ret;
+       page = (reg >> 6) & 0x3ff;
+       r = (reg >> 2) & 0xf;
+-      /* MT7530 uses 31 as the pseudo port */
+-      ret = bus->write(bus, 0x1f, 0x1f, page);
++      ret = bus->write(bus, priv->mdiodev->addr, 0x1f, page);
+       if (ret < 0)
+               return ret;
+-      lo = bus->read(bus, 0x1f, r);
+-      hi = bus->read(bus, 0x1f, 0x10);
++      lo = bus->read(bus, priv->mdiodev->addr, r);
++      hi = bus->read(bus, priv->mdiodev->addr, 0x10);
+       *val = (hi << 16) | (lo & 0xffff);
+@@ -107,8 +107,7 @@ mt7531_create_sgmii(struct mt7530_priv *
+               mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock;
+               mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock;
+-              regmap = devm_regmap_init(priv->dev,
+-                                        &mt7530_regmap_bus, priv->bus,
++              regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, priv,
+                                         mt7531_pcs_config[i]);
+               if (IS_ERR(regmap)) {
+                       ret = PTR_ERR(regmap);
+@@ -153,6 +152,7 @@ mt7530_probe(struct mdio_device *mdiodev
+       priv->bus = mdiodev->bus;
+       priv->dev = &mdiodev->dev;
++      priv->mdiodev = mdiodev;
+       ret = mt7530_probe_common(priv);
+       if (ret)
+@@ -203,8 +203,8 @@ mt7530_probe(struct mdio_device *mdiodev
+       regmap_config->reg_stride = 4;
+       regmap_config->max_register = MT7530_CREV;
+       regmap_config->disable_locking = true;
+-      priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus,
+-                                      priv->bus, regmap_config);
++      priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, priv,
++                                      regmap_config);
+       if (IS_ERR(priv->regmap))
+               return PTR_ERR(priv->regmap);
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -86,22 +86,26 @@ core_read_mmd_indirect(struct mt7530_pri
+       int value, ret;
+       /* Write the desired MMD Devad */
+-      ret = bus->write(bus, 0, MII_MMD_CTRL, devad);
++      ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                       MII_MMD_CTRL, devad);
+       if (ret < 0)
+               goto err;
+       /* Write the desired MMD register address */
+-      ret = bus->write(bus, 0, MII_MMD_DATA, prtad);
++      ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                       MII_MMD_DATA, prtad);
+       if (ret < 0)
+               goto err;
+       /* Select the Function : DATA with no post increment */
+-      ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
++      ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                       MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
+       if (ret < 0)
+               goto err;
+       /* Read the content of the MMD's selected register */
+-      value = bus->read(bus, 0, MII_MMD_DATA);
++      value = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                        MII_MMD_DATA);
+       return value;
+ err:
+@@ -118,22 +122,26 @@ core_write_mmd_indirect(struct mt7530_pr
+       int ret;
+       /* Write the desired MMD Devad */
+-      ret = bus->write(bus, 0, MII_MMD_CTRL, devad);
++      ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                       MII_MMD_CTRL, devad);
+       if (ret < 0)
+               goto err;
+       /* Write the desired MMD register address */
+-      ret = bus->write(bus, 0, MII_MMD_DATA, prtad);
++      ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                       MII_MMD_DATA, prtad);
+       if (ret < 0)
+               goto err;
+       /* Select the Function : DATA with no post increment */
+-      ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
++      ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                       MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
+       if (ret < 0)
+               goto err;
+       /* Write the data into MMD's selected register */
+-      ret = bus->write(bus, 0, MII_MMD_DATA, data);
++      ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                       MII_MMD_DATA, data);
+ err:
+       if (ret < 0)
+               dev_err(&bus->dev,
+@@ -2679,16 +2687,19 @@ mt7531_setup(struct dsa_switch *ds)
+        * phy_[read,write]_mmd_indirect is called, we provide our own
+        * mt7531_ind_mmd_phy_[read,write] to complete this function.
+        */
+-      val = mt7531_ind_c45_phy_read(priv, MT753X_CTRL_PHY_ADDR,
++      val = mt7531_ind_c45_phy_read(priv,
++                                    MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+                                     MDIO_MMD_VEND2, CORE_PLL_GROUP4);
+       val |= MT7531_RG_SYSPLL_DMY2 | MT7531_PHY_PLL_BYPASS_MODE;
+       val &= ~MT7531_PHY_PLL_OFF;
+-      mt7531_ind_c45_phy_write(priv, MT753X_CTRL_PHY_ADDR, MDIO_MMD_VEND2,
+-                               CORE_PLL_GROUP4, val);
++      mt7531_ind_c45_phy_write(priv,
++                               MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                               MDIO_MMD_VEND2, CORE_PLL_GROUP4, val);
+       /* Disable EEE advertisement on the switch PHYs. */
+-      for (i = MT753X_CTRL_PHY_ADDR;
+-           i < MT753X_CTRL_PHY_ADDR + MT7530_NUM_PHYS; i++) {
++      for (i = MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr);
++           i < MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr) + MT7530_NUM_PHYS;
++           i++) {
+               mt7531_ind_c45_phy_write(priv, i, MDIO_MMD_AN, MDIO_AN_EEE_ADV,
+                                        0);
+       }
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -629,7 +629,7 @@ enum mt7531_clk_skew {
+ #define  MT7531_PHY_PLL_OFF           BIT(5)
+ #define  MT7531_PHY_PLL_BYPASS_MODE   BIT(4)
+-#define MT753X_CTRL_PHY_ADDR          0
++#define MT753X_CTRL_PHY_ADDR(addr)    ((addr + 1) & 0x1f)
+ #define CORE_PLL_GROUP5                       0x404
+ #define  RG_LCDDS_PCW_NCPO1(x)                ((x) & 0xffff)
+@@ -778,6 +778,7 @@ struct mt753x_info {
+  * @irq_enable:               IRQ enable bits, synced to SYS_INT_EN
+  * @create_sgmii:     Pointer to function creating SGMII PCS instance(s)
+  * @active_cpu_ports: Holding the active CPU ports
++ * @mdiodev:          The pointer to the MDIO device structure
+  */
+ struct mt7530_priv {
+       struct device           *dev;
+@@ -804,6 +805,7 @@ struct mt7530_priv {
+       u32 irq_enable;
+       int (*create_sgmii)(struct mt7530_priv *priv);
+       u8 active_cpu_ports;
++      struct mdio_device *mdiodev;
+ };
+ struct mt7530_hw_vlan_entry {
diff --git a/target/linux/generic/backport-6.6/790-37-v6.10-net-dsa-mt7530-simplify-core-operations.patch b/target/linux/generic/backport-6.6/790-37-v6.10-net-dsa-mt7530-simplify-core-operations.patch
new file mode 100644 (file)
index 0000000..d9d70f1
--- /dev/null
@@ -0,0 +1,186 @@
+From 9764a08b3d260f4e7799d34bbfe64463db940d74 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Thu, 18 Apr 2024 08:35:31 +0300
+Subject: [PATCH 5/5] net: dsa: mt7530: simplify core operations
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The core_rmw() function calls core_read_mmd_indirect() to read the
+requested register, and then calls core_write_mmd_indirect() to write the
+requested value to the register. Because Clause 22 is used to access Clause
+45 registers, some operations on core_write_mmd_indirect() are
+unnecessarily run. Get rid of core_read_mmd_indirect() and
+core_write_mmd_indirect(), and run only the necessary operations on
+core_write() and core_rmw().
+
+Reviewed-by: Daniel Golle <daniel@makrotopia.org>
+Tested-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 108 ++++++++++++++++-----------------------
+ 1 file changed, 43 insertions(+), 65 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -74,116 +74,94 @@ static const struct mt7530_mib_desc mt75
+       MIB_DESC(1, 0xb8, "RxArlDrop"),
+ };
+-/* Since phy_device has not yet been created and
+- * phy_{read,write}_mmd_indirect is not available, we provide our own
+- * core_{read,write}_mmd_indirect with core_{clear,write,set} wrappers
+- * to complete this function.
+- */
+-static int
+-core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad)
++static void
++mt7530_mutex_lock(struct mt7530_priv *priv)
++{
++      if (priv->bus)
++              mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
++}
++
++static void
++mt7530_mutex_unlock(struct mt7530_priv *priv)
++{
++      if (priv->bus)
++              mutex_unlock(&priv->bus->mdio_lock);
++}
++
++static void
++core_write(struct mt7530_priv *priv, u32 reg, u32 val)
+ {
+       struct mii_bus *bus = priv->bus;
+-      int value, ret;
++      int ret;
++
++      mt7530_mutex_lock(priv);
+       /* Write the desired MMD Devad */
+       ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+-                       MII_MMD_CTRL, devad);
++                       MII_MMD_CTRL, MDIO_MMD_VEND2);
+       if (ret < 0)
+               goto err;
+       /* Write the desired MMD register address */
+       ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+-                       MII_MMD_DATA, prtad);
++                       MII_MMD_DATA, reg);
+       if (ret < 0)
+               goto err;
+       /* Select the Function : DATA with no post increment */
+       ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+-                       MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
++                       MII_MMD_CTRL, MDIO_MMD_VEND2 | MII_MMD_CTRL_NOINCR);
+       if (ret < 0)
+               goto err;
+-      /* Read the content of the MMD's selected register */
+-      value = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+-                        MII_MMD_DATA);
+-
+-      return value;
++      /* Write the data into MMD's selected register */
++      ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                       MII_MMD_DATA, val);
+ err:
+-      dev_err(&bus->dev,  "failed to read mmd register\n");
++      if (ret < 0)
++              dev_err(&bus->dev, "failed to write mmd register\n");
+-      return ret;
++      mt7530_mutex_unlock(priv);
+ }
+-static int
+-core_write_mmd_indirect(struct mt7530_priv *priv, int prtad,
+-                      int devad, u32 data)
++static void
++core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)
+ {
+       struct mii_bus *bus = priv->bus;
++      u32 val;
+       int ret;
++      mt7530_mutex_lock(priv);
++
+       /* Write the desired MMD Devad */
+       ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+-                       MII_MMD_CTRL, devad);
++                       MII_MMD_CTRL, MDIO_MMD_VEND2);
+       if (ret < 0)
+               goto err;
+       /* Write the desired MMD register address */
+       ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+-                       MII_MMD_DATA, prtad);
++                       MII_MMD_DATA, reg);
+       if (ret < 0)
+               goto err;
+       /* Select the Function : DATA with no post increment */
+       ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+-                       MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR);
++                       MII_MMD_CTRL, MDIO_MMD_VEND2 | MII_MMD_CTRL_NOINCR);
+       if (ret < 0)
+               goto err;
++      /* Read the content of the MMD's selected register */
++      val = bus->read(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
++                      MII_MMD_DATA);
++      val &= ~mask;
++      val |= set;
+       /* Write the data into MMD's selected register */
+       ret = bus->write(bus, MT753X_CTRL_PHY_ADDR(priv->mdiodev->addr),
+-                       MII_MMD_DATA, data);
++                       MII_MMD_DATA, val);
+ err:
+       if (ret < 0)
+-              dev_err(&bus->dev,
+-                      "failed to write mmd register\n");
+-      return ret;
+-}
+-
+-static void
+-mt7530_mutex_lock(struct mt7530_priv *priv)
+-{
+-      if (priv->bus)
+-              mutex_lock_nested(&priv->bus->mdio_lock, MDIO_MUTEX_NESTED);
+-}
+-
+-static void
+-mt7530_mutex_unlock(struct mt7530_priv *priv)
+-{
+-      if (priv->bus)
+-              mutex_unlock(&priv->bus->mdio_lock);
+-}
+-
+-static void
+-core_write(struct mt7530_priv *priv, u32 reg, u32 val)
+-{
+-      mt7530_mutex_lock(priv);
+-
+-      core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
+-
+-      mt7530_mutex_unlock(priv);
+-}
+-
+-static void
+-core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set)
+-{
+-      u32 val;
+-
+-      mt7530_mutex_lock(priv);
+-
+-      val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2);
+-      val &= ~mask;
+-      val |= set;
+-      core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val);
++              dev_err(&bus->dev, "failed to write mmd register\n");
+       mt7530_mutex_unlock(priv);
+ }
diff --git a/target/linux/generic/backport-6.6/790-38-v6.10-net-dsa-mt7530-disable-EEE-abilities-on-failure-on-M.patch b/target/linux/generic/backport-6.6/790-38-v6.10-net-dsa-mt7530-disable-EEE-abilities-on-failure-on-M.patch
new file mode 100644 (file)
index 0000000..44cf60c
--- /dev/null
@@ -0,0 +1,88 @@
+From 856e8954a0a88d1a4d2b43e9002b9249131a156f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:08 +0300
+Subject: [PATCH 01/15] net: dsa: mt7530: disable EEE abilities on failure on
+ MT7531 and MT7988
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The MT7531_FORCE_EEE1G and MT7531_FORCE_EEE100 bits let the
+PMCR_FORCE_EEE1G and PMCR_FORCE_EEE100 bits determine the 1G/100 EEE
+abilities of the MAC. If MT7531_FORCE_EEE1G and MT7531_FORCE_EEE100 are
+unset, the abilities are left to be determined by PHY auto polling.
+
+The commit 40b5d2f15c09 ("net: dsa: mt7530: Add support for EEE features")
+made it so that the PMCR_FORCE_EEE1G and PMCR_FORCE_EEE100 bits are set on
+mt753x_phylink_mac_link_up(). But it did not set the MT7531_FORCE_EEE1G and
+MT7531_FORCE_EEE100 bits. Because of this, the EEE abilities will be
+determined by PHY auto polling, regardless of the result of phy_init_eee().
+
+Define these bits and add them to the MT7531_FORCE_MODE mask which is set
+in mt7531_setup_common(). With this, there won't be any EEE abilities set
+when phy_init_eee() returns a negative value.
+
+Thanks to Russell for explaining when phy_init_eee() could return a
+negative value below.
+
+Looking at phy_init_eee(), it could return a negative value when:
+
+1. phydev->drv is NULL
+2. if genphy_c45_eee_is_active() returns negative
+3. if genphy_c45_eee_is_active() returns zero, it returns -EPROTONOSUPPORT
+4. if phy_set_bits_mmd() fails (e.g. communication error with the PHY)
+
+If we then look at genphy_c45_eee_is_active(), then:
+
+genphy_c45_read_eee_adv() and genphy_c45_read_eee_lpa() propagate their
+non-zero return values, otherwise this function returns zero or positive
+integer.
+
+If we then look at genphy_c45_read_eee_adv(), then a failure of
+phy_read_mmd() would cause a negative value to be returned.
+
+Looking at genphy_c45_read_eee_lpa(), the same is true.
+
+So, it can be summarised as:
+
+- phydev->drv is NULL
+- there is a communication error accessing the PHY
+- EEE is not active
+
+otherwise, it returns zero on success.
+
+If one wishes to determine whether an error occurred vs EEE not being
+supported through negotiation for the negotiated speed, if it returns
+-EPROTONOSUPPORT in the latter case. Other error codes mean either the
+driver has been unloaded or communication error.
+
+In conclusion, determining the EEE abilities by PHY auto polling shouldn't
+result in having any EEE abilities enabled, when one of the last two
+situations in the summary happens. And it seems that if phydev->drv is
+NULL, there would be bigger problems with the device than a broken link. So
+this is not a bugfix.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.h | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -328,11 +328,15 @@ enum mt7530_vlan_port_acc_frm {
+ #define  MT7531_FORCE_DPX             BIT(29)
+ #define  MT7531_FORCE_RX_FC           BIT(28)
+ #define  MT7531_FORCE_TX_FC           BIT(27)
++#define  MT7531_FORCE_EEE100          BIT(26)
++#define  MT7531_FORCE_EEE1G           BIT(25)
+ #define  MT7531_FORCE_MODE            (MT7531_FORCE_LNK | \
+                                        MT7531_FORCE_SPD | \
+                                        MT7531_FORCE_DPX | \
+                                        MT7531_FORCE_RX_FC | \
+-                                       MT7531_FORCE_TX_FC)
++                                       MT7531_FORCE_TX_FC | \
++                                       MT7531_FORCE_EEE100 | \
++                                       MT7531_FORCE_EEE1G)
+ #define  PMCR_LINK_SETTINGS_MASK      (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
+                                        PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
+                                        PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
diff --git a/target/linux/generic/backport-6.6/790-39-v6.10-net-dsa-mt7530-refactor-MT7530_PMCR_P.patch b/target/linux/generic/backport-6.6/790-39-v6.10-net-dsa-mt7530-refactor-MT7530_PMCR_P.patch
new file mode 100644 (file)
index 0000000..89fad45
--- /dev/null
@@ -0,0 +1,200 @@
+From 712ad00d2f43814c81a7abfcbc339690a05fb6a0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:09 +0300
+Subject: [PATCH 02/15] net: dsa: mt7530: refactor MT7530_PMCR_P()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The MT7530_PMCR_P() registers are on MT7530, MT7531, and the switch on the
+MT7988 SoC. Rename the definition for them to MT753X_PMCR_P(). Bit 15 is
+for MT7530 only. Add MT7530 prefix to the definition for bit 15.
+
+Use GENMASK and FIELD_PREP for PMCR_IFG_XMIT().
+
+Rename PMCR_TX_EN and PMCR_RX_EN to PMCR_MAC_TX_EN and PMCR_MAC_TX_EN to
+follow the naming on the "MT7621 Giga Switch Programming Guide v0.3",
+"MT7531 Reference Manual for Development Board v1.0", and "MT7988A Wi-Fi 7
+Generation Router Platform: Datasheet (Open Version) v0.1" documents.
+
+These documents show that PMCR_RX_FC_EN is at bit 5. Correct this along
+with renaming it to PMCR_FORCE_RX_FC_EN, and the same for PMCR_TX_FC_EN.
+
+Remove PMCR_SPEED_MASK which doesn't have a use.
+
+Rename the force mode definitions for MT7531 to FORCE_MODE. Add MASK at the
+end for the mask that includes all force mode definitions.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 24 ++++++++---------
+ drivers/net/dsa/mt7530.h | 58 +++++++++++++++++++++-------------------
+ 2 files changed, 42 insertions(+), 40 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -896,7 +896,7 @@ static void mt7530_setup_port5(struct ds
+               val &= ~MHWTRAP_P5_MAC_SEL & ~MHWTRAP_P5_DIS;
+               /* Setup the MAC by default for the cpu port */
+-              mt7530_write(priv, MT7530_PMCR_P(5), 0x56300);
++              mt7530_write(priv, MT753X_PMCR_P(5), 0x56300);
+               break;
+       case P5_INTF_SEL_GMAC5:
+               /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */
+@@ -2444,8 +2444,8 @@ mt7530_setup(struct dsa_switch *ds)
+               /* Clear link settings and enable force mode to force link down
+                * on all ports until they're enabled later.
+                */
+-              mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
+-                         PMCR_FORCE_MODE, PMCR_FORCE_MODE);
++              mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
++                         MT7530_FORCE_MODE, MT7530_FORCE_MODE);
+               /* Disable forwarding by default on all ports */
+               mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
+@@ -2555,8 +2555,8 @@ mt7531_setup_common(struct dsa_switch *d
+               /* Clear link settings and enable force mode to force link down
+                * on all ports until they're enabled later.
+                */
+-              mt7530_rmw(priv, MT7530_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
+-                         MT7531_FORCE_MODE, MT7531_FORCE_MODE);
++              mt7530_rmw(priv, MT753X_PMCR_P(i), PMCR_LINK_SETTINGS_MASK |
++                         MT7531_FORCE_MODE_MASK, MT7531_FORCE_MODE_MASK);
+               /* Disable forwarding by default on all ports */
+               mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,
+@@ -2639,7 +2639,7 @@ mt7531_setup(struct dsa_switch *ds)
+       /* Force link down on all ports before internal reset */
+       for (i = 0; i < MT7530_NUM_PORTS; i++)
+-              mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK);
++              mt7530_write(priv, MT753X_PMCR_P(i), MT7531_FORCE_MODE_LNK);
+       /* Reset the switch through internal reset */
+       mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
+@@ -2881,7 +2881,7 @@ mt753x_phylink_mac_config(struct phylink
+       /* Are we connected to external phy */
+       if (port == 5 && dsa_is_user_port(ds, 5))
+-              mt7530_set(priv, MT7530_PMCR_P(port), PMCR_EXT_PHY);
++              mt7530_set(priv, MT753X_PMCR_P(port), PMCR_EXT_PHY);
+ }
+ static void mt753x_phylink_mac_link_down(struct phylink_config *config,
+@@ -2891,7 +2891,7 @@ static void mt753x_phylink_mac_link_down
+       struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct mt7530_priv *priv = dp->ds->priv;
+-      mt7530_clear(priv, MT7530_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK);
++      mt7530_clear(priv, MT753X_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK);
+ }
+ static void mt753x_phylink_mac_link_up(struct phylink_config *config,
+@@ -2905,7 +2905,7 @@ static void mt753x_phylink_mac_link_up(s
+       struct mt7530_priv *priv = dp->ds->priv;
+       u32 mcr;
+-      mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK;
++      mcr = PMCR_MAC_RX_EN | PMCR_MAC_TX_EN | PMCR_FORCE_LNK;
+       switch (speed) {
+       case SPEED_1000:
+@@ -2920,9 +2920,9 @@ static void mt753x_phylink_mac_link_up(s
+       if (duplex == DUPLEX_FULL) {
+               mcr |= PMCR_FORCE_FDX;
+               if (tx_pause)
+-                      mcr |= PMCR_TX_FC_EN;
++                      mcr |= PMCR_FORCE_TX_FC_EN;
+               if (rx_pause)
+-                      mcr |= PMCR_RX_FC_EN;
++                      mcr |= PMCR_FORCE_RX_FC_EN;
+       }
+       if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) {
+@@ -2937,7 +2937,7 @@ static void mt753x_phylink_mac_link_up(s
+               }
+       }
+-      mt7530_set(priv, MT7530_PMCR_P(dp->index), mcr);
++      mt7530_set(priv, MT753X_PMCR_P(dp->index), mcr);
+ }
+ static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -304,44 +304,46 @@ enum mt7530_vlan_port_acc_frm {
+ #define  G0_PORT_VID_DEF              G0_PORT_VID(0)
+ /* Register for port MAC control register */
+-#define MT7530_PMCR_P(x)              (0x3000 + ((x) * 0x100))
+-#define  PMCR_IFG_XMIT(x)             (((x) & 0x3) << 18)
++#define MT753X_PMCR_P(x)              (0x3000 + ((x) * 0x100))
++#define  PMCR_IFG_XMIT_MASK           GENMASK(19, 18)
++#define  PMCR_IFG_XMIT(x)             FIELD_PREP(PMCR_IFG_XMIT_MASK, x)
+ #define  PMCR_EXT_PHY                 BIT(17)
+ #define  PMCR_MAC_MODE                        BIT(16)
+-#define  PMCR_FORCE_MODE              BIT(15)
+-#define  PMCR_TX_EN                   BIT(14)
+-#define  PMCR_RX_EN                   BIT(13)
++#define  MT7530_FORCE_MODE            BIT(15)
++#define  PMCR_MAC_TX_EN                       BIT(14)
++#define  PMCR_MAC_RX_EN                       BIT(13)
+ #define  PMCR_BACKOFF_EN              BIT(9)
+ #define  PMCR_BACKPR_EN                       BIT(8)
+ #define  PMCR_FORCE_EEE1G             BIT(7)
+ #define  PMCR_FORCE_EEE100            BIT(6)
+-#define  PMCR_TX_FC_EN                        BIT(5)
+-#define  PMCR_RX_FC_EN                        BIT(4)
++#define  PMCR_FORCE_RX_FC_EN          BIT(5)
++#define  PMCR_FORCE_TX_FC_EN          BIT(4)
+ #define  PMCR_FORCE_SPEED_1000                BIT(3)
+ #define  PMCR_FORCE_SPEED_100         BIT(2)
+ #define  PMCR_FORCE_FDX                       BIT(1)
+ #define  PMCR_FORCE_LNK                       BIT(0)
+-#define  PMCR_SPEED_MASK              (PMCR_FORCE_SPEED_100 | \
+-                                       PMCR_FORCE_SPEED_1000)
+-#define  MT7531_FORCE_LNK             BIT(31)
+-#define  MT7531_FORCE_SPD             BIT(30)
+-#define  MT7531_FORCE_DPX             BIT(29)
+-#define  MT7531_FORCE_RX_FC           BIT(28)
+-#define  MT7531_FORCE_TX_FC           BIT(27)
+-#define  MT7531_FORCE_EEE100          BIT(26)
+-#define  MT7531_FORCE_EEE1G           BIT(25)
+-#define  MT7531_FORCE_MODE            (MT7531_FORCE_LNK | \
+-                                       MT7531_FORCE_SPD | \
+-                                       MT7531_FORCE_DPX | \
+-                                       MT7531_FORCE_RX_FC | \
+-                                       MT7531_FORCE_TX_FC | \
+-                                       MT7531_FORCE_EEE100 | \
+-                                       MT7531_FORCE_EEE1G)
+-#define  PMCR_LINK_SETTINGS_MASK      (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
+-                                       PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
+-                                       PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
+-                                       PMCR_FORCE_FDX | PMCR_FORCE_LNK | \
+-                                       PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100)
++#define  MT7531_FORCE_MODE_LNK                BIT(31)
++#define  MT7531_FORCE_MODE_SPD                BIT(30)
++#define  MT7531_FORCE_MODE_DPX                BIT(29)
++#define  MT7531_FORCE_MODE_RX_FC      BIT(28)
++#define  MT7531_FORCE_MODE_TX_FC      BIT(27)
++#define  MT7531_FORCE_MODE_EEE100     BIT(26)
++#define  MT7531_FORCE_MODE_EEE1G      BIT(25)
++#define  MT7531_FORCE_MODE_MASK               (MT7531_FORCE_MODE_LNK | \
++                                       MT7531_FORCE_MODE_SPD | \
++                                       MT7531_FORCE_MODE_DPX | \
++                                       MT7531_FORCE_MODE_RX_FC | \
++                                       MT7531_FORCE_MODE_TX_FC | \
++                                       MT7531_FORCE_MODE_EEE100 | \
++                                       MT7531_FORCE_MODE_EEE1G)
++#define  PMCR_LINK_SETTINGS_MASK      (PMCR_MAC_TX_EN | PMCR_MAC_RX_EN | \
++                                       PMCR_FORCE_EEE1G | \
++                                       PMCR_FORCE_EEE100 | \
++                                       PMCR_FORCE_RX_FC_EN | \
++                                       PMCR_FORCE_TX_FC_EN | \
++                                       PMCR_FORCE_SPEED_1000 | \
++                                       PMCR_FORCE_SPEED_100 | \
++                                       PMCR_FORCE_FDX | PMCR_FORCE_LNK)
+ #define MT7530_PMEEECR_P(x)           (0x3004 + (x) * 0x100)
+ #define  WAKEUP_TIME_1000(x)          (((x) & 0xFF) << 24)
diff --git a/target/linux/generic/backport-6.6/790-40-v6.10-net-dsa-mt7530-rename-p5_intf_sel-and-use-only-for-M.patch b/target/linux/generic/backport-6.6/790-40-v6.10-net-dsa-mt7530-rename-p5_intf_sel-and-use-only-for-M.patch
new file mode 100644 (file)
index 0000000..601171a
--- /dev/null
@@ -0,0 +1,185 @@
+From 875ec5b67ab88e969b171e6e9ea803e3ed759614 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:10 +0300
+Subject: [PATCH 03/15] net: dsa: mt7530: rename p5_intf_sel and use only for
+ MT7530 switch
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The p5_intf_sel pointer is used to store the information of whether PHY
+muxing is used or not. PHY muxing is a feature specific to port 5 of the
+MT7530 switch. Do not use it for other switch models.
+
+Rename the pointer to p5_mode to store the mode the port is being used in.
+Rename the p5_interface_select enum to mt7530_p5_mode, the string
+representation to mt7530_p5_mode_str, and the enum elements.
+
+If PHY muxing is not detected, the default mode, GMAC5, will be used.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 62 +++++++++++++++++-----------------------
+ drivers/net/dsa/mt7530.h | 15 +++++-----
+ 2 files changed, 33 insertions(+), 44 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -857,19 +857,15 @@ mt7530_set_ageing_time(struct dsa_switch
+       return 0;
+ }
+-static const char *p5_intf_modes(unsigned int p5_interface)
++static const char *mt7530_p5_mode_str(unsigned int mode)
+ {
+-      switch (p5_interface) {
+-      case P5_DISABLED:
+-              return "DISABLED";
+-      case P5_INTF_SEL_PHY_P0:
+-              return "PHY P0";
+-      case P5_INTF_SEL_PHY_P4:
+-              return "PHY P4";
+-      case P5_INTF_SEL_GMAC5:
+-              return "GMAC5";
++      switch (mode) {
++      case MUX_PHY_P0:
++              return "MUX PHY P0";
++      case MUX_PHY_P4:
++              return "MUX PHY P4";
+       default:
+-              return "unknown";
++              return "GMAC5";
+       }
+ }
+@@ -886,23 +882,23 @@ static void mt7530_setup_port5(struct ds
+       val |= MHWTRAP_MANUAL | MHWTRAP_P5_MAC_SEL | MHWTRAP_P5_DIS;
+       val &= ~MHWTRAP_P5_RGMII_MODE & ~MHWTRAP_PHY0_SEL;
+-      switch (priv->p5_intf_sel) {
+-      case P5_INTF_SEL_PHY_P0:
+-              /* MT7530_P5_MODE_GPHY_P0: 2nd GMAC -> P5 -> P0 */
++      switch (priv->p5_mode) {
++      /* MUX_PHY_P0: P0 -> P5 -> SoC MAC */
++      case MUX_PHY_P0:
+               val |= MHWTRAP_PHY0_SEL;
+               fallthrough;
+-      case P5_INTF_SEL_PHY_P4:
+-              /* MT7530_P5_MODE_GPHY_P4: 2nd GMAC -> P5 -> P4 */
++
++      /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */
++      case MUX_PHY_P4:
+               val &= ~MHWTRAP_P5_MAC_SEL & ~MHWTRAP_P5_DIS;
+               /* Setup the MAC by default for the cpu port */
+               mt7530_write(priv, MT753X_PMCR_P(5), 0x56300);
+               break;
+-      case P5_INTF_SEL_GMAC5:
+-              /* MT7530_P5_MODE_GMAC: P5 -> External phy or 2nd GMAC */
+-              val &= ~MHWTRAP_P5_DIS;
+-              break;
++
++      /* GMAC5: P5 -> SoC MAC or external PHY */
+       default:
++              val &= ~MHWTRAP_P5_DIS;
+               break;
+       }
+@@ -930,8 +926,8 @@ static void mt7530_setup_port5(struct ds
+       mt7530_write(priv, MT7530_MHWTRAP, val);
+-      dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, intf_sel=%s, phy-mode=%s\n",
+-              val, p5_intf_modes(priv->p5_intf_sel), phy_modes(interface));
++      dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, mode=%s, phy-mode=%s\n", val,
++              mt7530_p5_mode_str(priv->p5_mode), phy_modes(interface));
+       mutex_unlock(&priv->reg_mutex);
+ }
+@@ -2476,13 +2472,11 @@ mt7530_setup(struct dsa_switch *ds)
+       if (ret)
+               return ret;
+-      /* Setup port 5 */
+-      if (!dsa_is_unused_port(ds, 5)) {
+-              priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
+-      } else {
++      /* Check for PHY muxing on port 5 */
++      if (dsa_is_unused_port(ds, 5)) {
+               /* Scan the ethernet nodes. Look for GMAC1, lookup the used PHY.
+-               * Set priv->p5_intf_sel to the appropriate value if PHY muxing
+-               * is detected.
++               * Set priv->p5_mode to the appropriate value if PHY muxing is
++               * detected.
+                */
+               for_each_child_of_node(dn, mac_np) {
+                       if (!of_device_is_compatible(mac_np,
+@@ -2506,17 +2500,16 @@ mt7530_setup(struct dsa_switch *ds)
+                               }
+                               id = of_mdio_parse_addr(ds->dev, phy_node);
+                               if (id == 0)
+-                                      priv->p5_intf_sel = P5_INTF_SEL_PHY_P0;
++                                      priv->p5_mode = MUX_PHY_P0;
+                               if (id == 4)
+-                                      priv->p5_intf_sel = P5_INTF_SEL_PHY_P4;
++                                      priv->p5_mode = MUX_PHY_P4;
+                       }
+                       of_node_put(mac_np);
+                       of_node_put(phy_node);
+                       break;
+               }
+-              if (priv->p5_intf_sel == P5_INTF_SEL_PHY_P0 ||
+-                  priv->p5_intf_sel == P5_INTF_SEL_PHY_P4)
++              if (priv->p5_mode == MUX_PHY_P0 || priv->p5_mode == MUX_PHY_P4)
+                       mt7530_setup_port5(ds, interface);
+       }
+@@ -2654,9 +2647,6 @@ mt7531_setup(struct dsa_switch *ds)
+                          MT7531_EXT_P_MDIO_12);
+       }
+-      if (!dsa_is_unused_port(ds, 5))
+-              priv->p5_intf_sel = P5_INTF_SEL_GMAC5;
+-
+       mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK,
+                  MT7531_GPIO0_INTERRUPT);
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -708,12 +708,11 @@ struct mt7530_port {
+       struct phylink_pcs *sgmii_pcs;
+ };
+-/* Port 5 interface select definitions */
+-enum p5_interface_select {
+-      P5_DISABLED,
+-      P5_INTF_SEL_PHY_P0,
+-      P5_INTF_SEL_PHY_P4,
+-      P5_INTF_SEL_GMAC5,
++/* Port 5 mode definitions of the MT7530 switch */
++enum mt7530_p5_mode {
++      GMAC5,
++      MUX_PHY_P0,
++      MUX_PHY_P4,
+ };
+ struct mt7530_priv;
+@@ -776,7 +775,7 @@ struct mt753x_info {
+  * @ports:            Holding the state among ports
+  * @reg_mutex:                The lock for protecting among process accessing
+  *                    registers
+- * @p5_intf_sel:      Holding the current port 5 interface select
++ * @p5_mode:          Holding the current mode of port 5 of the MT7530 switch
+  * @p5_sgmii:         Flag for distinguishing if port 5 of the MT7531 switch
+  *                    has got SGMII
+  * @irq:              IRQ number of the switch
+@@ -798,7 +797,7 @@ struct mt7530_priv {
+       const struct mt753x_info *info;
+       unsigned int            id;
+       bool                    mcm;
+-      enum p5_interface_select p5_intf_sel;
++      enum mt7530_p5_mode     p5_mode;
+       bool                    p5_sgmii;
+       u8                      mirror_rx;
+       u8                      mirror_tx;
diff --git a/target/linux/generic/backport-6.6/790-41-v6.10-net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch b/target/linux/generic/backport-6.6/790-41-v6.10-net-dsa-mt7530-rename-mt753x_bpdu_port_fw-enum-to-mt.patch
new file mode 100644 (file)
index 0000000..948baf5
--- /dev/null
@@ -0,0 +1,169 @@
+From 83fe3df057e641cd0e88425e579d7a5a370ca430 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:11 +0300
+Subject: [PATCH 04/15] net: dsa: mt7530: rename mt753x_bpdu_port_fw enum to
+ mt753x_to_cpu_fw
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The mt753x_bpdu_port_fw enum is globally used for manipulating the process
+of deciding the forwardable ports, specifically concerning the CPU port(s).
+Therefore, rename it and the values in it to mt753x_to_cpu_fw.
+
+Change FOLLOW_MFC to SYSTEM_DEFAULT to be on par with the switch documents.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 44 ++++++++++-------------
+ drivers/net/dsa/mt7530.h | 76 ++++++++++++++++++++--------------------
+ 2 files changed, 56 insertions(+), 64 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1107,42 +1107,34 @@ mt753x_trap_frames(struct mt7530_priv *p
+        * VLAN-untagged.
+        */
+       mt7530_rmw(priv, MT753X_BPC,
+-                 MT753X_PAE_BPDU_FR | MT753X_PAE_EG_TAG_MASK |
+-                         MT753X_PAE_PORT_FW_MASK | MT753X_BPDU_EG_TAG_MASK |
+-                         MT753X_BPDU_PORT_FW_MASK,
+-                 MT753X_PAE_BPDU_FR |
+-                         MT753X_PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+-                         MT753X_PAE_PORT_FW(MT753X_BPDU_CPU_ONLY) |
+-                         MT753X_BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+-                         MT753X_BPDU_CPU_ONLY);
++                 PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK |
++                         BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK,
++                 PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
++                         PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) |
++                         BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
++                         TO_CPU_FW_CPU_ONLY);
+       /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress
+        * them VLAN-untagged.
+        */
+       mt7530_rmw(priv, MT753X_RGAC1,
+-                 MT753X_R02_BPDU_FR | MT753X_R02_EG_TAG_MASK |
+-                         MT753X_R02_PORT_FW_MASK | MT753X_R01_BPDU_FR |
+-                         MT753X_R01_EG_TAG_MASK | MT753X_R01_PORT_FW_MASK,
+-                 MT753X_R02_BPDU_FR |
+-                         MT753X_R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+-                         MT753X_R02_PORT_FW(MT753X_BPDU_CPU_ONLY) |
+-                         MT753X_R01_BPDU_FR |
+-                         MT753X_R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+-                         MT753X_BPDU_CPU_ONLY);
++                 R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK |
++                         R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK,
++                 R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
++                         R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR |
++                         R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
++                         TO_CPU_FW_CPU_ONLY);
+       /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress
+        * them VLAN-untagged.
+        */
+       mt7530_rmw(priv, MT753X_RGAC2,
+-                 MT753X_R0E_BPDU_FR | MT753X_R0E_EG_TAG_MASK |
+-                         MT753X_R0E_PORT_FW_MASK | MT753X_R03_BPDU_FR |
+-                         MT753X_R03_EG_TAG_MASK | MT753X_R03_PORT_FW_MASK,
+-                 MT753X_R0E_BPDU_FR |
+-                         MT753X_R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+-                         MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY) |
+-                         MT753X_R03_BPDU_FR |
+-                         MT753X_R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+-                         MT753X_BPDU_CPU_ONLY);
++                 R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK |
++                         R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK,
++                 R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
++                         R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR |
++                         R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
++                         TO_CPU_FW_CPU_ONLY);
+ }
+ static void
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -67,47 +67,47 @@ enum mt753x_id {
+ #define MT753X_MIRROR_MASK(id)                ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
+                                        MT7531_MIRROR_MASK : MIRROR_MASK)
+-/* Registers for BPDU and PAE frame control*/
++/* Register for BPDU and PAE frame control */
+ #define MT753X_BPC                    0x24
+-#define  MT753X_PAE_BPDU_FR           BIT(25)
+-#define  MT753X_PAE_EG_TAG_MASK               GENMASK(24, 22)
+-#define  MT753X_PAE_EG_TAG(x)         FIELD_PREP(MT753X_PAE_EG_TAG_MASK, x)
+-#define  MT753X_PAE_PORT_FW_MASK      GENMASK(18, 16)
+-#define  MT753X_PAE_PORT_FW(x)                FIELD_PREP(MT753X_PAE_PORT_FW_MASK, x)
+-#define  MT753X_BPDU_EG_TAG_MASK      GENMASK(8, 6)
+-#define  MT753X_BPDU_EG_TAG(x)                FIELD_PREP(MT753X_BPDU_EG_TAG_MASK, x)
+-#define  MT753X_BPDU_PORT_FW_MASK     GENMASK(2, 0)
++#define  PAE_BPDU_FR                  BIT(25)
++#define  PAE_EG_TAG_MASK              GENMASK(24, 22)
++#define  PAE_EG_TAG(x)                        FIELD_PREP(PAE_EG_TAG_MASK, x)
++#define  PAE_PORT_FW_MASK             GENMASK(18, 16)
++#define  PAE_PORT_FW(x)                       FIELD_PREP(PAE_PORT_FW_MASK, x)
++#define  BPDU_EG_TAG_MASK             GENMASK(8, 6)
++#define  BPDU_EG_TAG(x)                       FIELD_PREP(BPDU_EG_TAG_MASK, x)
++#define  BPDU_PORT_FW_MASK            GENMASK(2, 0)
+-/* Register for :01 and :02 MAC DA frame control */
++/* Register for 01-80-C2-00-00-[01,02] MAC DA frame control */
+ #define MT753X_RGAC1                  0x28
+-#define  MT753X_R02_BPDU_FR           BIT(25)
+-#define  MT753X_R02_EG_TAG_MASK               GENMASK(24, 22)
+-#define  MT753X_R02_EG_TAG(x)         FIELD_PREP(MT753X_R02_EG_TAG_MASK, x)
+-#define  MT753X_R02_PORT_FW_MASK      GENMASK(18, 16)
+-#define  MT753X_R02_PORT_FW(x)                FIELD_PREP(MT753X_R02_PORT_FW_MASK, x)
+-#define  MT753X_R01_BPDU_FR           BIT(9)
+-#define  MT753X_R01_EG_TAG_MASK               GENMASK(8, 6)
+-#define  MT753X_R01_EG_TAG(x)         FIELD_PREP(MT753X_R01_EG_TAG_MASK, x)
+-#define  MT753X_R01_PORT_FW_MASK      GENMASK(2, 0)
++#define  R02_BPDU_FR                  BIT(25)
++#define  R02_EG_TAG_MASK              GENMASK(24, 22)
++#define  R02_EG_TAG(x)                        FIELD_PREP(R02_EG_TAG_MASK, x)
++#define  R02_PORT_FW_MASK             GENMASK(18, 16)
++#define  R02_PORT_FW(x)                       FIELD_PREP(R02_PORT_FW_MASK, x)
++#define  R01_BPDU_FR                  BIT(9)
++#define  R01_EG_TAG_MASK              GENMASK(8, 6)
++#define  R01_EG_TAG(x)                        FIELD_PREP(R01_EG_TAG_MASK, x)
++#define  R01_PORT_FW_MASK             GENMASK(2, 0)
+-/* Register for :03 and :0E MAC DA frame control */
++/* Register for 01-80-C2-00-00-[03,0E] MAC DA frame control */
+ #define MT753X_RGAC2                  0x2c
+-#define  MT753X_R0E_BPDU_FR           BIT(25)
+-#define  MT753X_R0E_EG_TAG_MASK               GENMASK(24, 22)
+-#define  MT753X_R0E_EG_TAG(x)         FIELD_PREP(MT753X_R0E_EG_TAG_MASK, x)
+-#define  MT753X_R0E_PORT_FW_MASK      GENMASK(18, 16)
+-#define  MT753X_R0E_PORT_FW(x)                FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x)
+-#define  MT753X_R03_BPDU_FR           BIT(9)
+-#define  MT753X_R03_EG_TAG_MASK               GENMASK(8, 6)
+-#define  MT753X_R03_EG_TAG(x)         FIELD_PREP(MT753X_R03_EG_TAG_MASK, x)
+-#define  MT753X_R03_PORT_FW_MASK      GENMASK(2, 0)
++#define  R0E_BPDU_FR                  BIT(25)
++#define  R0E_EG_TAG_MASK              GENMASK(24, 22)
++#define  R0E_EG_TAG(x)                        FIELD_PREP(R0E_EG_TAG_MASK, x)
++#define  R0E_PORT_FW_MASK             GENMASK(18, 16)
++#define  R0E_PORT_FW(x)                       FIELD_PREP(R0E_PORT_FW_MASK, x)
++#define  R03_BPDU_FR                  BIT(9)
++#define  R03_EG_TAG_MASK              GENMASK(8, 6)
++#define  R03_EG_TAG(x)                        FIELD_PREP(R03_EG_TAG_MASK, x)
++#define  R03_PORT_FW_MASK             GENMASK(2, 0)
+-enum mt753x_bpdu_port_fw {
+-      MT753X_BPDU_FOLLOW_MFC,
+-      MT753X_BPDU_CPU_EXCLUDE = 4,
+-      MT753X_BPDU_CPU_INCLUDE = 5,
+-      MT753X_BPDU_CPU_ONLY = 6,
+-      MT753X_BPDU_DROP = 7,
++enum mt753x_to_cpu_fw {
++      TO_CPU_FW_SYSTEM_DEFAULT,
++      TO_CPU_FW_CPU_EXCLUDE = 4,
++      TO_CPU_FW_CPU_INCLUDE = 5,
++      TO_CPU_FW_CPU_ONLY = 6,
++      TO_CPU_FW_DROP = 7,
+ };
+ /* Registers for address table access */
diff --git a/target/linux/generic/backport-6.6/790-42-v6.10-net-dsa-mt7530-refactor-MT7530_MFC-and-MT7531_CFC-ad.patch b/target/linux/generic/backport-6.6/790-42-v6.10-net-dsa-mt7530-refactor-MT7530_MFC-and-MT7531_CFC-ad.patch
new file mode 100644 (file)
index 0000000..a5d293b
--- /dev/null
@@ -0,0 +1,201 @@
+From 1dbc1bdc2869e6d2929235c70d64e393aa5a5fa2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:12 +0300
+Subject: [PATCH 05/15] net: dsa: mt7530: refactor MT7530_MFC and MT7531_CFC,
+ add MT7531_QRY_FFP
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The MT7530_MFC register is on MT7530, MT7531, and the switch on the MT7988
+SoC. Rename it to MT753X_MFC. Bit 7 to 0 differs between MT7530 and
+MT7531/MT7988. Add MT7530 prefix to these definitions, and define the
+IGMP/MLD Query Frame Flooding Ports mask for MT7531.
+
+Rename the cases of MIRROR_MASK to MIRROR_PORT_MASK.
+
+Move mt753x_mirror_port_get() and mt753x_port_mirror_set() to mt7530.h as
+macros.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 38 ++++++++--------------
+ drivers/net/dsa/mt7530.h | 69 +++++++++++++++++++++++++---------------
+ 2 files changed, 57 insertions(+), 50 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1147,7 +1147,7 @@ mt753x_cpu_port_enable(struct dsa_switch
+                    PORT_SPEC_TAG);
+       /* Enable flooding on the CPU port */
+-      mt7530_set(priv, MT7530_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) |
++      mt7530_set(priv, MT753X_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) |
+                  UNU_FFP(BIT(port)));
+       /* Add the CPU port to the CPU port bitmap for MT7531 and the switch on
+@@ -1311,15 +1311,15 @@ mt7530_port_bridge_flags(struct dsa_swit
+                          flags.val & BR_LEARNING ? 0 : SA_DIS);
+       if (flags.mask & BR_FLOOD)
+-              mt7530_rmw(priv, MT7530_MFC, UNU_FFP(BIT(port)),
++              mt7530_rmw(priv, MT753X_MFC, UNU_FFP(BIT(port)),
+                          flags.val & BR_FLOOD ? UNU_FFP(BIT(port)) : 0);
+       if (flags.mask & BR_MCAST_FLOOD)
+-              mt7530_rmw(priv, MT7530_MFC, UNM_FFP(BIT(port)),
++              mt7530_rmw(priv, MT753X_MFC, UNM_FFP(BIT(port)),
+                          flags.val & BR_MCAST_FLOOD ? UNM_FFP(BIT(port)) : 0);
+       if (flags.mask & BR_BCAST_FLOOD)
+-              mt7530_rmw(priv, MT7530_MFC, BC_FFP(BIT(port)),
++              mt7530_rmw(priv, MT753X_MFC, BC_FFP(BIT(port)),
+                          flags.val & BR_BCAST_FLOOD ? BC_FFP(BIT(port)) : 0);
+       return 0;
+@@ -1855,20 +1855,6 @@ mt7530_port_vlan_del(struct dsa_switch *
+       return 0;
+ }
+-static int mt753x_mirror_port_get(unsigned int id, u32 val)
+-{
+-      return (id == ID_MT7531 || id == ID_MT7988) ?
+-                     MT7531_MIRROR_PORT_GET(val) :
+-                     MIRROR_PORT(val);
+-}
+-
+-static int mt753x_mirror_port_set(unsigned int id, u32 val)
+-{
+-      return (id == ID_MT7531 || id == ID_MT7988) ?
+-                     MT7531_MIRROR_PORT_SET(val) :
+-                     MIRROR_PORT(val);
+-}
+-
+ static int mt753x_port_mirror_add(struct dsa_switch *ds, int port,
+                                 struct dsa_mall_mirror_tc_entry *mirror,
+                                 bool ingress, struct netlink_ext_ack *extack)
+@@ -1884,14 +1870,14 @@ static int mt753x_port_mirror_add(struct
+       val = mt7530_read(priv, MT753X_MIRROR_REG(priv->id));
+       /* MT7530 only supports one monitor port */
+-      monitor_port = mt753x_mirror_port_get(priv->id, val);
++      monitor_port = MT753X_MIRROR_PORT_GET(priv->id, val);
+       if (val & MT753X_MIRROR_EN(priv->id) &&
+           monitor_port != mirror->to_local_port)
+               return -EEXIST;
+       val |= MT753X_MIRROR_EN(priv->id);
+-      val &= ~MT753X_MIRROR_MASK(priv->id);
+-      val |= mt753x_mirror_port_set(priv->id, mirror->to_local_port);
++      val &= ~MT753X_MIRROR_PORT_MASK(priv->id);
++      val |= MT753X_MIRROR_PORT_SET(priv->id, mirror->to_local_port);
+       mt7530_write(priv, MT753X_MIRROR_REG(priv->id), val);
+       val = mt7530_read(priv, MT7530_PCR_P(port));
+@@ -2533,7 +2519,7 @@ mt7531_setup_common(struct dsa_switch *d
+       mt7530_mib_reset(ds);
+       /* Disable flooding on all ports */
+-      mt7530_clear(priv, MT7530_MFC, BC_FFP_MASK | UNM_FFP_MASK |
++      mt7530_clear(priv, MT753X_MFC, BC_FFP_MASK | UNM_FFP_MASK |
+                    UNU_FFP_MASK);
+       for (i = 0; i < MT7530_NUM_PORTS; i++) {
+@@ -3089,10 +3075,12 @@ mt753x_conduit_state_change(struct dsa_s
+       else
+               priv->active_cpu_ports &= ~mask;
+-      if (priv->active_cpu_ports)
+-              val = CPU_EN | CPU_PORT(__ffs(priv->active_cpu_ports));
++      if (priv->active_cpu_ports) {
++              val = MT7530_CPU_EN |
++                    MT7530_CPU_PORT(__ffs(priv->active_cpu_ports));
++      }
+-      mt7530_rmw(priv, MT7530_MFC, CPU_EN | CPU_PORT_MASK, val);
++      mt7530_rmw(priv, MT753X_MFC, MT7530_CPU_EN | MT7530_CPU_PORT_MASK, val);
+ }
+ static int mt7988_setup(struct dsa_switch *ds)
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -36,36 +36,55 @@ enum mt753x_id {
+ #define MT753X_AGC                    0xc
+ #define  LOCAL_EN                     BIT(7)
+-/* Registers to mac forward control for unknown frames */
+-#define MT7530_MFC                    0x10
+-#define  BC_FFP(x)                    (((x) & 0xff) << 24)
+-#define  BC_FFP_MASK                  BC_FFP(~0)
+-#define  UNM_FFP(x)                   (((x) & 0xff) << 16)
+-#define  UNM_FFP_MASK                 UNM_FFP(~0)
+-#define  UNU_FFP(x)                   (((x) & 0xff) << 8)
+-#define  UNU_FFP_MASK                 UNU_FFP(~0)
+-#define  CPU_EN                               BIT(7)
+-#define  CPU_PORT_MASK                        GENMASK(6, 4)
+-#define  CPU_PORT(x)                  FIELD_PREP(CPU_PORT_MASK, x)
+-#define  MIRROR_EN                    BIT(3)
+-#define  MIRROR_PORT(x)                       ((x) & 0x7)
+-#define  MIRROR_MASK                  0x7
++/* Register for MAC forward control */
++#define MT753X_MFC                    0x10
++#define  BC_FFP_MASK                  GENMASK(31, 24)
++#define  BC_FFP(x)                    FIELD_PREP(BC_FFP_MASK, x)
++#define  UNM_FFP_MASK                 GENMASK(23, 16)
++#define  UNM_FFP(x)                   FIELD_PREP(UNM_FFP_MASK, x)
++#define  UNU_FFP_MASK                 GENMASK(15, 8)
++#define  UNU_FFP(x)                   FIELD_PREP(UNU_FFP_MASK, x)
++#define  MT7530_CPU_EN                        BIT(7)
++#define  MT7530_CPU_PORT_MASK         GENMASK(6, 4)
++#define  MT7530_CPU_PORT(x)           FIELD_PREP(MT7530_CPU_PORT_MASK, x)
++#define  MT7530_MIRROR_EN             BIT(3)
++#define  MT7530_MIRROR_PORT_MASK      GENMASK(2, 0)
++#define  MT7530_MIRROR_PORT_GET(x)    FIELD_GET(MT7530_MIRROR_PORT_MASK, x)
++#define  MT7530_MIRROR_PORT_SET(x)    FIELD_PREP(MT7530_MIRROR_PORT_MASK, x)
++#define  MT7531_QRY_FFP_MASK          GENMASK(7, 0)
++#define  MT7531_QRY_FFP(x)            FIELD_PREP(MT7531_QRY_FFP_MASK, x)
+-/* Registers for CPU forward control */
++/* Register for CPU forward control */
+ #define MT7531_CFC                    0x4
+ #define  MT7531_MIRROR_EN             BIT(19)
+-#define  MT7531_MIRROR_MASK           (MIRROR_MASK << 16)
+-#define  MT7531_MIRROR_PORT_GET(x)    (((x) >> 16) & MIRROR_MASK)
+-#define  MT7531_MIRROR_PORT_SET(x)    (((x) & MIRROR_MASK) << 16)
++#define  MT7531_MIRROR_PORT_MASK      GENMASK(18, 16)
++#define  MT7531_MIRROR_PORT_GET(x)    FIELD_GET(MT7531_MIRROR_PORT_MASK, x)
++#define  MT7531_MIRROR_PORT_SET(x)    FIELD_PREP(MT7531_MIRROR_PORT_MASK, x)
+ #define  MT7531_CPU_PMAP_MASK         GENMASK(7, 0)
+ #define  MT7531_CPU_PMAP(x)           FIELD_PREP(MT7531_CPU_PMAP_MASK, x)
+-#define MT753X_MIRROR_REG(id)         ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
+-                                       MT7531_CFC : MT7530_MFC)
+-#define MT753X_MIRROR_EN(id)          ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
+-                                       MT7531_MIRROR_EN : MIRROR_EN)
+-#define MT753X_MIRROR_MASK(id)                ((((id) == ID_MT7531) || ((id) == ID_MT7988)) ? \
+-                                       MT7531_MIRROR_MASK : MIRROR_MASK)
++#define MT753X_MIRROR_REG(id)         ((id == ID_MT7531 || \
++                                        id == ID_MT7988) ? \
++                                       MT7531_CFC : MT753X_MFC)
++
++#define MT753X_MIRROR_EN(id)          ((id == ID_MT7531 || \
++                                        id == ID_MT7988) ? \
++                                       MT7531_MIRROR_EN : MT7530_MIRROR_EN)
++
++#define MT753X_MIRROR_PORT_MASK(id)   ((id == ID_MT7531 || \
++                                        id == ID_MT7988) ? \
++                                       MT7531_MIRROR_PORT_MASK : \
++                                       MT7530_MIRROR_PORT_MASK)
++
++#define MT753X_MIRROR_PORT_GET(id, val)       ((id == ID_MT7531 || \
++                                        id == ID_MT7988) ? \
++                                       MT7531_MIRROR_PORT_GET(val) : \
++                                       MT7530_MIRROR_PORT_GET(val))
++
++#define MT753X_MIRROR_PORT_SET(id, val)       ((id == ID_MT7531 || \
++                                        id == ID_MT7988) ? \
++                                       MT7531_MIRROR_PORT_SET(val) : \
++                                       MT7530_MIRROR_PORT_SET(val))
+ /* Register for BPDU and PAE frame control */
+ #define MT753X_BPC                    0x24
diff --git a/target/linux/generic/backport-6.6/790-43-v6.10-net-dsa-mt7530-refactor-MT7530_HWTRAP-and-MT7530_MHW.patch b/target/linux/generic/backport-6.6/790-43-v6.10-net-dsa-mt7530-refactor-MT7530_HWTRAP-and-MT7530_MHW.patch
new file mode 100644 (file)
index 0000000..1f66575
--- /dev/null
@@ -0,0 +1,257 @@
+From 3ccf67597d35c06a7319e407b1c42f78a7966779 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:13 +0300
+Subject: [PATCH 06/15] net: dsa: mt7530: refactor MT7530_HWTRAP and
+ MT7530_MHWTRAP
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The MT7530_HWTRAP and MT7530_MHWTRAP registers are on MT7530 and MT7531.
+It's called hardware trap on MT7530, software trap on MT7531. That's
+because some bits of the trap on MT7530 cannot be modified by software
+whilst all bits of the trap on MT7531 can. Rename the definitions for them
+to MT753X_TRAP and MT753X_MTRAP. Add MT7530 and MT7531 prefixes to the
+definitions specific to the switch model.
+
+Remove the extra parentheses from MT7530_XTAL_40MHZ and MT7530_XTAL_20MHZ.
+
+Rename MHWTRAP_PHY0_SEL, MHWTRAP_MANUAL, and MHWTRAP_PHY_ACCESS to be on
+par with the "MT7621 Giga Switch Programming Guide v0.3" document.
+
+Make an enumaration for the XTAL frequency. Set the data type of the xtal
+variable on mt7531_pll_setup() to it.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 59 ++++++++++++++++++++--------------------
+ drivers/net/dsa/mt7530.h | 50 ++++++++++++++++------------------
+ 2 files changed, 54 insertions(+), 55 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -417,23 +417,23 @@ mt7530_setup_port6(struct dsa_switch *ds
+       mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1));
+-      xtal = mt7530_read(priv, MT7530_MHWTRAP) & HWTRAP_XTAL_MASK;
++      xtal = mt7530_read(priv, MT753X_MTRAP) & MT7530_XTAL_MASK;
+-      if (xtal == HWTRAP_XTAL_25MHZ)
++      if (xtal == MT7530_XTAL_25MHZ)
+               ssc_delta = 0x57;
+       else
+               ssc_delta = 0x87;
+       if (priv->id == ID_MT7621) {
+               /* PLL frequency: 125MHz: 1.0GBit */
+-              if (xtal == HWTRAP_XTAL_40MHZ)
++              if (xtal == MT7530_XTAL_40MHZ)
+                       ncpo1 = 0x0640;
+-              if (xtal == HWTRAP_XTAL_25MHZ)
++              if (xtal == MT7530_XTAL_25MHZ)
+                       ncpo1 = 0x0a00;
+       } else { /* PLL frequency: 250MHz: 2.0Gbit */
+-              if (xtal == HWTRAP_XTAL_40MHZ)
++              if (xtal == MT7530_XTAL_40MHZ)
+                       ncpo1 = 0x0c80;
+-              if (xtal == HWTRAP_XTAL_25MHZ)
++              if (xtal == MT7530_XTAL_25MHZ)
+                       ncpo1 = 0x1400;
+       }
+@@ -456,19 +456,20 @@ mt7530_setup_port6(struct dsa_switch *ds
+ static void
+ mt7531_pll_setup(struct mt7530_priv *priv)
+ {
++      enum mt7531_xtal_fsel xtal;
+       u32 top_sig;
+       u32 hwstrap;
+-      u32 xtal;
+       u32 val;
+       val = mt7530_read(priv, MT7531_CREV);
+       top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR);
+-      hwstrap = mt7530_read(priv, MT7531_HWTRAP);
++      hwstrap = mt7530_read(priv, MT753X_TRAP);
+       if ((val & CHIP_REV_M) > 0)
+-              xtal = (top_sig & PAD_MCM_SMI_EN) ? HWTRAP_XTAL_FSEL_40MHZ :
+-                                                  HWTRAP_XTAL_FSEL_25MHZ;
++              xtal = (top_sig & PAD_MCM_SMI_EN) ? MT7531_XTAL_FSEL_40MHZ :
++                                                  MT7531_XTAL_FSEL_25MHZ;
+       else
+-              xtal = hwstrap & HWTRAP_XTAL_FSEL_MASK;
++              xtal = (hwstrap & MT7531_XTAL25) ? MT7531_XTAL_FSEL_25MHZ :
++                                                 MT7531_XTAL_FSEL_40MHZ;
+       /* Step 1 : Disable MT7531 COREPLL */
+       val = mt7530_read(priv, MT7531_PLLGP_EN);
+@@ -497,13 +498,13 @@ mt7531_pll_setup(struct mt7530_priv *pri
+       usleep_range(25, 35);
+       switch (xtal) {
+-      case HWTRAP_XTAL_FSEL_25MHZ:
++      case MT7531_XTAL_FSEL_25MHZ:
+               val = mt7530_read(priv, MT7531_PLLGP_CR0);
+               val &= ~RG_COREPLL_SDM_PCW_M;
+               val |= 0x140000 << RG_COREPLL_SDM_PCW_S;
+               mt7530_write(priv, MT7531_PLLGP_CR0, val);
+               break;
+-      case HWTRAP_XTAL_FSEL_40MHZ:
++      case MT7531_XTAL_FSEL_40MHZ:
+               val = mt7530_read(priv, MT7531_PLLGP_CR0);
+               val &= ~RG_COREPLL_SDM_PCW_M;
+               val |= 0x190000 << RG_COREPLL_SDM_PCW_S;
+@@ -877,20 +878,20 @@ static void mt7530_setup_port5(struct ds
+       mutex_lock(&priv->reg_mutex);
+-      val = mt7530_read(priv, MT7530_MHWTRAP);
++      val = mt7530_read(priv, MT753X_MTRAP);
+-      val |= MHWTRAP_MANUAL | MHWTRAP_P5_MAC_SEL | MHWTRAP_P5_DIS;
+-      val &= ~MHWTRAP_P5_RGMII_MODE & ~MHWTRAP_PHY0_SEL;
++      val |= MT7530_CHG_TRAP | MT7530_P5_MAC_SEL | MT7530_P5_DIS;
++      val &= ~MT7530_P5_RGMII_MODE & ~MT7530_P5_PHY0_SEL;
+       switch (priv->p5_mode) {
+       /* MUX_PHY_P0: P0 -> P5 -> SoC MAC */
+       case MUX_PHY_P0:
+-              val |= MHWTRAP_PHY0_SEL;
++              val |= MT7530_P5_PHY0_SEL;
+               fallthrough;
+       /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */
+       case MUX_PHY_P4:
+-              val &= ~MHWTRAP_P5_MAC_SEL & ~MHWTRAP_P5_DIS;
++              val &= ~MT7530_P5_MAC_SEL & ~MT7530_P5_DIS;
+               /* Setup the MAC by default for the cpu port */
+               mt7530_write(priv, MT753X_PMCR_P(5), 0x56300);
+@@ -898,13 +899,13 @@ static void mt7530_setup_port5(struct ds
+       /* GMAC5: P5 -> SoC MAC or external PHY */
+       default:
+-              val &= ~MHWTRAP_P5_DIS;
++              val &= ~MT7530_P5_DIS;
+               break;
+       }
+       /* Setup RGMII settings */
+       if (phy_interface_mode_is_rgmii(interface)) {
+-              val |= MHWTRAP_P5_RGMII_MODE;
++              val |= MT7530_P5_RGMII_MODE;
+               /* P5 RGMII RX Clock Control: delay setting for 1000M */
+               mt7530_write(priv, MT7530_P5RGMIIRXCR, CSR_RGMII_EDGE_ALIGN);
+@@ -924,7 +925,7 @@ static void mt7530_setup_port5(struct ds
+                            P5_IO_CLK_DRV(1) | P5_IO_DATA_DRV(1));
+       }
+-      mt7530_write(priv, MT7530_MHWTRAP, val);
++      mt7530_write(priv, MT753X_MTRAP, val);
+       dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, mode=%s, phy-mode=%s\n", val,
+               mt7530_p5_mode_str(priv->p5_mode), phy_modes(interface));
+@@ -2365,7 +2366,7 @@ mt7530_setup(struct dsa_switch *ds)
+       }
+       /* Waiting for MT7530 got to stable */
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP);
++      INIT_MT7530_DUMMY_POLL(&p, priv, MT753X_TRAP);
+       ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0,
+                                20, 1000000);
+       if (ret < 0) {
+@@ -2380,7 +2381,7 @@ mt7530_setup(struct dsa_switch *ds)
+               return -ENODEV;
+       }
+-      if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_20MHZ) {
++      if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_20MHZ) {
+               dev_err(priv->dev,
+                       "MT7530 with a 20MHz XTAL is not supported!\n");
+               return -EINVAL;
+@@ -2401,12 +2402,12 @@ mt7530_setup(struct dsa_switch *ds)
+                          RD_TAP_MASK, RD_TAP(16));
+       /* Enable port 6 */
+-      val = mt7530_read(priv, MT7530_MHWTRAP);
+-      val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS;
+-      val |= MHWTRAP_MANUAL;
+-      mt7530_write(priv, MT7530_MHWTRAP, val);
++      val = mt7530_read(priv, MT753X_MTRAP);
++      val &= ~MT7530_P6_DIS & ~MT7530_PHY_INDIRECT_ACCESS;
++      val |= MT7530_CHG_TRAP;
++      mt7530_write(priv, MT753X_MTRAP, val);
+-      if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_40MHZ)
++      if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_40MHZ)
+               mt7530_pll_setup(priv);
+       mt753x_trap_frames(priv);
+@@ -2586,7 +2587,7 @@ mt7531_setup(struct dsa_switch *ds)
+       }
+       /* Waiting for MT7530 got to stable */
+-      INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP);
++      INIT_MT7530_DUMMY_POLL(&p, priv, MT753X_TRAP);
+       ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0,
+                                20, 1000000);
+       if (ret < 0) {
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -495,32 +495,30 @@ enum mt7531_clk_skew {
+       MT7531_CLK_SKEW_REVERSE = 3,
+ };
+-/* Register for hw trap status */
+-#define MT7530_HWTRAP                 0x7800
+-#define  HWTRAP_XTAL_MASK             (BIT(10) | BIT(9))
+-#define  HWTRAP_XTAL_25MHZ            (BIT(10) | BIT(9))
+-#define  HWTRAP_XTAL_40MHZ            (BIT(10))
+-#define  HWTRAP_XTAL_20MHZ            (BIT(9))
++/* Register for trap status */
++#define MT753X_TRAP                   0x7800
++#define  MT7530_XTAL_MASK             (BIT(10) | BIT(9))
++#define  MT7530_XTAL_25MHZ            (BIT(10) | BIT(9))
++#define  MT7530_XTAL_40MHZ            BIT(10)
++#define  MT7530_XTAL_20MHZ            BIT(9)
++#define  MT7531_XTAL25                        BIT(7)
+-#define MT7531_HWTRAP                 0x7800
+-#define  HWTRAP_XTAL_FSEL_MASK                BIT(7)
+-#define  HWTRAP_XTAL_FSEL_25MHZ               BIT(7)
+-#define  HWTRAP_XTAL_FSEL_40MHZ               0
+-/* Unique fields of (M)HWSTRAP for MT7531 */
+-#define  XTAL_FSEL_S                  7
+-#define  XTAL_FSEL_M                  BIT(7)
+-#define  PHY_EN                               BIT(6)
+-#define  CHG_STRAP                    BIT(8)
++/* Register for trap modification */
++#define MT753X_MTRAP                  0x7804
++#define  MT7530_P5_PHY0_SEL           BIT(20)
++#define  MT7530_CHG_TRAP              BIT(16)
++#define  MT7530_P5_MAC_SEL            BIT(13)
++#define  MT7530_P6_DIS                        BIT(8)
++#define  MT7530_P5_RGMII_MODE         BIT(7)
++#define  MT7530_P5_DIS                        BIT(6)
++#define  MT7530_PHY_INDIRECT_ACCESS   BIT(5)
++#define  MT7531_CHG_STRAP             BIT(8)
++#define  MT7531_PHY_EN                        BIT(6)
+-/* Register for hw trap modification */
+-#define MT7530_MHWTRAP                        0x7804
+-#define  MHWTRAP_PHY0_SEL             BIT(20)
+-#define  MHWTRAP_MANUAL                       BIT(16)
+-#define  MHWTRAP_P5_MAC_SEL           BIT(13)
+-#define  MHWTRAP_P6_DIS                       BIT(8)
+-#define  MHWTRAP_P5_RGMII_MODE                BIT(7)
+-#define  MHWTRAP_P5_DIS                       BIT(6)
+-#define  MHWTRAP_PHY_ACCESS           BIT(5)
++enum mt7531_xtal_fsel {
++      MT7531_XTAL_FSEL_25MHZ,
++      MT7531_XTAL_FSEL_40MHZ,
++};
+ /* Register for TOP signal control */
+ #define MT7530_TOP_SIG_CTRL           0x7808
diff --git a/target/linux/generic/backport-6.6/790-44-v6.10-net-dsa-mt7530-move-MT753X_MTRAP-operations-for-MT75.patch b/target/linux/generic/backport-6.6/790-44-v6.10-net-dsa-mt7530-move-MT753X_MTRAP-operations-for-MT75.patch
new file mode 100644 (file)
index 0000000..f7802c0
--- /dev/null
@@ -0,0 +1,117 @@
+From 2982f395c9a513b168f1e685588f70013cba2f5f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:14 +0300
+Subject: [PATCH 07/15] net: dsa: mt7530: move MT753X_MTRAP operations for
+ MT7530
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+On MT7530, the media-independent interfaces of port 5 and 6 are controlled
+by the MT7530_P5_DIS and MT7530_P6_DIS bits of the hardware trap. Deal with
+these bits only when the relevant port is being enabled or disabled. This
+ensures that these ports will be disabled when they are not in use.
+
+Do not set MT7530_CHG_TRAP on mt7530_setup_port5() as that's already being
+done on mt7530_setup().
+
+Instead of globally setting MT7530_P5_MAC_SEL, clear it, then set it only
+on the appropriate case.
+
+If PHY muxing is detected, clear MT7530_P5_DIS before calling
+mt7530_setup_port5().
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 38 +++++++++++++++++++++++++++-----------
+ 1 file changed, 27 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -880,8 +880,7 @@ static void mt7530_setup_port5(struct ds
+       val = mt7530_read(priv, MT753X_MTRAP);
+-      val |= MT7530_CHG_TRAP | MT7530_P5_MAC_SEL | MT7530_P5_DIS;
+-      val &= ~MT7530_P5_RGMII_MODE & ~MT7530_P5_PHY0_SEL;
++      val &= ~MT7530_P5_PHY0_SEL & ~MT7530_P5_MAC_SEL & ~MT7530_P5_RGMII_MODE;
+       switch (priv->p5_mode) {
+       /* MUX_PHY_P0: P0 -> P5 -> SoC MAC */
+@@ -891,15 +890,13 @@ static void mt7530_setup_port5(struct ds
+       /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */
+       case MUX_PHY_P4:
+-              val &= ~MT7530_P5_MAC_SEL & ~MT7530_P5_DIS;
+-
+               /* Setup the MAC by default for the cpu port */
+               mt7530_write(priv, MT753X_PMCR_P(5), 0x56300);
+               break;
+       /* GMAC5: P5 -> SoC MAC or external PHY */
+       default:
+-              val &= ~MT7530_P5_DIS;
++              val |= MT7530_P5_MAC_SEL;
+               break;
+       }
+@@ -1193,6 +1190,14 @@ mt7530_port_enable(struct dsa_switch *ds
+       mutex_unlock(&priv->reg_mutex);
++      if (priv->id != ID_MT7530 && priv->id != ID_MT7621)
++              return 0;
++
++      if (port == 5)
++              mt7530_clear(priv, MT753X_MTRAP, MT7530_P5_DIS);
++      else if (port == 6)
++              mt7530_clear(priv, MT753X_MTRAP, MT7530_P6_DIS);
++
+       return 0;
+ }
+@@ -1211,6 +1216,14 @@ mt7530_port_disable(struct dsa_switch *d
+                  PCR_MATRIX_CLR);
+       mutex_unlock(&priv->reg_mutex);
++
++      if (priv->id != ID_MT7530 && priv->id != ID_MT7621)
++              return;
++
++      if (port == 5)
++              mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS);
++      else if (port == 6)
++              mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS);
+ }
+ static int
+@@ -2401,11 +2414,11 @@ mt7530_setup(struct dsa_switch *ds)
+               mt7530_rmw(priv, MT7530_TRGMII_RD(i),
+                          RD_TAP_MASK, RD_TAP(16));
+-      /* Enable port 6 */
+-      val = mt7530_read(priv, MT753X_MTRAP);
+-      val &= ~MT7530_P6_DIS & ~MT7530_PHY_INDIRECT_ACCESS;
+-      val |= MT7530_CHG_TRAP;
+-      mt7530_write(priv, MT753X_MTRAP, val);
++      /* Allow modifying the trap and directly access PHY registers via the
++       * MDIO bus the switch is on.
++       */
++      mt7530_rmw(priv, MT753X_MTRAP, MT7530_CHG_TRAP |
++                 MT7530_PHY_INDIRECT_ACCESS, MT7530_CHG_TRAP);
+       if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_40MHZ)
+               mt7530_pll_setup(priv);
+@@ -2488,8 +2501,11 @@ mt7530_setup(struct dsa_switch *ds)
+                       break;
+               }
+-              if (priv->p5_mode == MUX_PHY_P0 || priv->p5_mode == MUX_PHY_P4)
++              if (priv->p5_mode == MUX_PHY_P0 ||
++                  priv->p5_mode == MUX_PHY_P4) {
++                      mt7530_clear(priv, MT753X_MTRAP, MT7530_P5_DIS);
+                       mt7530_setup_port5(ds, interface);
++              }
+       }
+ #ifdef CONFIG_GPIOLIB
diff --git a/target/linux/generic/backport-6.6/790-45-v6.10-net-dsa-mt7530-return-mt7530_setup_mdio-mt7531_setup.patch b/target/linux/generic/backport-6.6/790-45-v6.10-net-dsa-mt7530-return-mt7530_setup_mdio-mt7531_setup.patch
new file mode 100644 (file)
index 0000000..2eaa77c
--- /dev/null
@@ -0,0 +1,39 @@
+From 1f5669efca65564c7533704917f79003c6b36c9c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:15 +0300
+Subject: [PATCH 08/15] net: dsa: mt7530: return mt7530_setup_mdio &
+ mt7531_setup_common on error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The mt7530_setup_mdio() and mt7531_setup_common() functions should be
+checked for errors. Return if the functions return a non-zero value.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2667,7 +2667,9 @@ mt7531_setup(struct dsa_switch *ds)
+                                        0);
+       }
+-      mt7531_setup_common(ds);
++      ret = mt7531_setup_common(ds);
++      if (ret)
++              return ret;
+       /* Setup VLAN ID 0 for VLAN-unaware bridges */
+       ret = mt7530_setup_vlan0(priv);
+@@ -3020,6 +3022,8 @@ mt753x_setup(struct dsa_switch *ds)
+       ret = mt7530_setup_mdio(priv);
+       if (ret && priv->irq)
+               mt7530_free_irq_common(priv);
++      if (ret)
++              return ret;
+       /* Initialise the PCS devices */
+       for (i = 0; i < priv->ds->num_ports; i++) {
diff --git a/target/linux/generic/backport-6.6/790-46-v6.10-net-dsa-mt7530-define-MAC-speed-capabilities-per-swi.patch b/target/linux/generic/backport-6.6/790-46-v6.10-net-dsa-mt7530-define-MAC-speed-capabilities-per-swi.patch
new file mode 100644 (file)
index 0000000..9a592c7
--- /dev/null
@@ -0,0 +1,75 @@
+From 6cc2d4ccd77509df74b7b8ef46bbc6ba0a571318 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:16 +0300
+Subject: [PATCH 09/15] net: dsa: mt7530: define MAC speed capabilities per
+ switch model
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+With the support of the MT7988 SoC switch, the MAC speed capabilities
+defined on mt753x_phylink_get_caps() won't apply to all switch models
+anymore. Move them to more appropriate locations instead of overwriting
+config->mac_capabilities.
+
+Remove the comment on mt753x_phylink_get_caps() as it's become invalid with
+the support of MT7531 and MT7988 SoC switch.
+
+Add break to case 6 of mt7988_mac_port_get_caps() to be explicit.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 15 ++++++++++-----
+ 1 file changed, 10 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2685,6 +2685,8 @@ mt7531_setup(struct dsa_switch *ds)
+ static void mt7530_mac_port_get_caps(struct dsa_switch *ds, int port,
+                                    struct phylink_config *config)
+ {
++      config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD;
++
+       switch (port) {
+       /* Ports which are connected to switch PHYs. There is no MII pinout. */
+       case 0 ... 4:
+@@ -2716,6 +2718,8 @@ static void mt7531_mac_port_get_caps(str
+ {
+       struct mt7530_priv *priv = ds->priv;
++      config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD;
++
+       switch (port) {
+       /* Ports which are connected to switch PHYs. There is no MII pinout. */
+       case 0 ... 4:
+@@ -2755,14 +2759,17 @@ static void mt7988_mac_port_get_caps(str
+       case 0 ... 3:
+               __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+                         config->supported_interfaces);
++
++              config->mac_capabilities |= MAC_10 | MAC_100 | MAC_1000FD;
+               break;
+       /* Port 6 is connected to SoC's XGMII MAC. There is no MII pinout. */
+       case 6:
+               __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+                         config->supported_interfaces);
+-              config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+-                                         MAC_10000FD;
++
++              config->mac_capabilities |= MAC_10000FD;
++              break;
+       }
+ }
+@@ -2932,9 +2939,7 @@ static void mt753x_phylink_get_caps(stru
+ {
+       struct mt7530_priv *priv = ds->priv;
+-      /* This switch only supports full-duplex at 1Gbps */
+-      config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+-                                 MAC_10 | MAC_100 | MAC_1000FD;
++      config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
+       priv->info->mac_port_get_caps(ds, port, config);
+ }
diff --git a/target/linux/generic/backport-6.6/790-47-v6.10-net-dsa-mt7530-get-rid-of-function-sanity-check.patch b/target/linux/generic/backport-6.6/790-47-v6.10-net-dsa-mt7530-get-rid-of-function-sanity-check.patch
new file mode 100644 (file)
index 0000000..bc84ecb
--- /dev/null
@@ -0,0 +1,33 @@
+From dd0f15fc877c10567699190bce0f55e96f4ad6b5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:17 +0300
+Subject: [PATCH 10/15] net: dsa: mt7530: get rid of function sanity check
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Get rid of checking whether functions are filled properly. priv->info which
+is an mt753x_info structure is filled and checked for before this check.
+It's unnecessary checking whether it's filled properly.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 7 -------
+ 1 file changed, 7 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -3232,13 +3232,6 @@ mt7530_probe_common(struct mt7530_priv *
+       if (!priv->info)
+               return -EINVAL;
+-      /* Sanity check if these required device operations are filled
+-       * properly.
+-       */
+-      if (!priv->info->sw_setup || !priv->info->phy_read_c22 ||
+-          !priv->info->phy_write_c22 || !priv->info->mac_port_get_caps)
+-              return -EINVAL;
+-
+       priv->id = priv->info->id;
+       priv->dev = dev;
+       priv->ds->priv = priv;
diff --git a/target/linux/generic/backport-6.6/790-48-v6.10-net-dsa-mt7530-refactor-MT7530_PMEEECR_P.patch b/target/linux/generic/backport-6.6/790-48-v6.10-net-dsa-mt7530-refactor-MT7530_PMEEECR_P.patch
new file mode 100644 (file)
index 0000000..e75db9b
--- /dev/null
@@ -0,0 +1,71 @@
+From 2dff9759602b069f97ccc939e15a47ca051b2983 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:18 +0300
+Subject: [PATCH 11/15] net: dsa: mt7530: refactor MT7530_PMEEECR_P()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The MT7530_PMEEECR_P() register is on MT7530, MT7531, and the switch on the
+MT7988 SoC. Rename the definition for them to MT753X_PMEEECR_P(). Use the
+FIELD_PREP and FIELD_GET macros. Rename GET_LPI_THRESH() and
+SET_LPI_THRESH() to LPI_THRESH_GET() and LPI_THRESH_SET().
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c |  8 ++++----
+ drivers/net/dsa/mt7530.h | 13 +++++++------
+ 2 files changed, 11 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -3051,10 +3051,10 @@ static int mt753x_get_mac_eee(struct dsa
+                             struct ethtool_eee *e)
+ {
+       struct mt7530_priv *priv = ds->priv;
+-      u32 eeecr = mt7530_read(priv, MT7530_PMEEECR_P(port));
++      u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
+       e->tx_lpi_enabled = !(eeecr & LPI_MODE_EN);
+-      e->tx_lpi_timer = GET_LPI_THRESH(eeecr);
++      e->tx_lpi_timer = LPI_THRESH_GET(eeecr);
+       return 0;
+ }
+@@ -3068,11 +3068,11 @@ static int mt753x_set_mac_eee(struct dsa
+       if (e->tx_lpi_timer > 0xFFF)
+               return -EINVAL;
+-      set = SET_LPI_THRESH(e->tx_lpi_timer);
++      set = LPI_THRESH_SET(e->tx_lpi_timer);
+       if (!e->tx_lpi_enabled)
+               /* Force LPI Mode without a delay */
+               set |= LPI_MODE_EN;
+-      mt7530_rmw(priv, MT7530_PMEEECR_P(port), mask, set);
++      mt7530_rmw(priv, MT753X_PMEEECR_P(port), mask, set);
+       return 0;
+ }
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -364,13 +364,14 @@ enum mt7530_vlan_port_acc_frm {
+                                        PMCR_FORCE_SPEED_100 | \
+                                        PMCR_FORCE_FDX | PMCR_FORCE_LNK)
+-#define MT7530_PMEEECR_P(x)           (0x3004 + (x) * 0x100)
+-#define  WAKEUP_TIME_1000(x)          (((x) & 0xFF) << 24)
+-#define  WAKEUP_TIME_100(x)           (((x) & 0xFF) << 16)
++#define MT753X_PMEEECR_P(x)           (0x3004 + (x) * 0x100)
++#define  WAKEUP_TIME_1000_MASK                GENMASK(31, 24)
++#define  WAKEUP_TIME_1000(x)          FIELD_PREP(WAKEUP_TIME_1000_MASK, x)
++#define  WAKEUP_TIME_100_MASK         GENMASK(23, 16)
++#define  WAKEUP_TIME_100(x)           FIELD_PREP(WAKEUP_TIME_100_MASK, x)
+ #define  LPI_THRESH_MASK              GENMASK(15, 4)
+-#define  LPI_THRESH_SHT                       4
+-#define  SET_LPI_THRESH(x)            (((x) << LPI_THRESH_SHT) & LPI_THRESH_MASK)
+-#define  GET_LPI_THRESH(x)            (((x) & LPI_THRESH_MASK) >> LPI_THRESH_SHT)
++#define  LPI_THRESH_GET(x)            FIELD_GET(LPI_THRESH_MASK, x)
++#define  LPI_THRESH_SET(x)            FIELD_PREP(LPI_THRESH_MASK, x)
+ #define  LPI_MODE_EN                  BIT(0)
+ #define MT7530_PMSR_P(x)              (0x3008 + (x) * 0x100)
diff --git a/target/linux/generic/backport-6.6/790-49-v6.10-net-dsa-mt7530-get-rid-of-mac_port_validate-member-o.patch b/target/linux/generic/backport-6.6/790-49-v6.10-net-dsa-mt7530-get-rid-of-mac_port_validate-member-o.patch
new file mode 100644 (file)
index 0000000..d083708
--- /dev/null
@@ -0,0 +1,48 @@
+From 21d67c2fabfe40baf33202d3287b67b6c16f8382 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:19 +0300
+Subject: [PATCH 12/15] net: dsa: mt7530: get rid of mac_port_validate member
+ of mt753x_info
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The mac_port_validate member of the mt753x_info structure is not being
+used, remove it. Improve the member description section in the process.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.h | 10 +++-------
+ 1 file changed, 3 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -743,15 +743,14 @@ struct mt753x_pcs {
+ /* struct mt753x_info -       This is the main data structure for holding the specific
+  *                    part for each supported device
++ * @id:                       Holding the identifier to a switch model
++ * @pcs_ops:          Holding the pointer to the MAC PCS operations structure
+  * @sw_setup:         Holding the handler to a device initialization
+  * @phy_read_c22:     Holding the way reading PHY port using C22
+  * @phy_write_c22:    Holding the way writing PHY port using C22
+  * @phy_read_c45:     Holding the way reading PHY port using C45
+  * @phy_write_c45:    Holding the way writing PHY port using C45
+- * @phy_mode_supported:       Check if the PHY type is being supported on a certain
+- *                    port
+- * @mac_port_validate:        Holding the way to set addition validate type for a
+- *                    certan MAC port
++ * @mac_port_get_caps:        Holding the handler that provides MAC capabilities
+  * @mac_port_config:  Holding the way setting up the PHY attribute to a
+  *                    certain MAC port
+  */
+@@ -770,9 +769,6 @@ struct mt753x_info {
+                            int regnum, u16 val);
+       void (*mac_port_get_caps)(struct dsa_switch *ds, int port,
+                                 struct phylink_config *config);
+-      void (*mac_port_validate)(struct dsa_switch *ds, int port,
+-                                phy_interface_t interface,
+-                                unsigned long *supported);
+       void (*mac_port_config)(struct dsa_switch *ds, int port,
+                               unsigned int mode,
+                               phy_interface_t interface);
diff --git a/target/linux/generic/backport-6.6/790-50-v6.10-net-dsa-mt7530-use-priv-ds-num_ports-instead-of-MT75.patch b/target/linux/generic/backport-6.6/790-50-v6.10-net-dsa-mt7530-use-priv-ds-num_ports-instead-of-MT75.patch
new file mode 100644 (file)
index 0000000..f63d4d7
--- /dev/null
@@ -0,0 +1,57 @@
+From 6efc8ae3eb0363328f479191a0cf0dc12a16e090 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:20 +0300
+Subject: [PATCH 13/15] net: dsa: mt7530: use priv->ds->num_ports instead of
+ MT7530_NUM_PORTS
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Use priv->ds->num_ports on all for loops which configure the switch
+registers. In the future, the value of MT7530_NUM_PORTS will depend on
+priv->id. Therefore, this change prepares the subdriver for a simpler
+implementation.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1411,7 +1411,7 @@ mt7530_port_set_vlan_unaware(struct dsa_
+       mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
+                  G0_PORT_VID_DEF);
+-      for (i = 0; i < MT7530_NUM_PORTS; i++) {
++      for (i = 0; i < priv->ds->num_ports; i++) {
+               if (dsa_is_user_port(ds, i) &&
+                   dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) {
+                       all_user_ports_removed = false;
+@@ -2428,7 +2428,7 @@ mt7530_setup(struct dsa_switch *ds)
+       /* Enable and reset MIB counters */
+       mt7530_mib_reset(ds);
+-      for (i = 0; i < MT7530_NUM_PORTS; i++) {
++      for (i = 0; i < priv->ds->num_ports; i++) {
+               /* Clear link settings and enable force mode to force link down
+                * on all ports until they're enabled later.
+                */
+@@ -2539,7 +2539,7 @@ mt7531_setup_common(struct dsa_switch *d
+       mt7530_clear(priv, MT753X_MFC, BC_FFP_MASK | UNM_FFP_MASK |
+                    UNU_FFP_MASK);
+-      for (i = 0; i < MT7530_NUM_PORTS; i++) {
++      for (i = 0; i < priv->ds->num_ports; i++) {
+               /* Clear link settings and enable force mode to force link down
+                * on all ports until they're enabled later.
+                */
+@@ -2626,7 +2626,7 @@ mt7531_setup(struct dsa_switch *ds)
+       priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN);
+       /* Force link down on all ports before internal reset */
+-      for (i = 0; i < MT7530_NUM_PORTS; i++)
++      for (i = 0; i < priv->ds->num_ports; i++)
+               mt7530_write(priv, MT753X_PMCR_P(i), MT7531_FORCE_MODE_LNK);
+       /* Reset the switch through internal reset */
diff --git a/target/linux/generic/backport-6.6/790-51-v6.10-net-dsa-mt7530-do-not-pass-port-variable-to-mt7531_r.patch b/target/linux/generic/backport-6.6/790-51-v6.10-net-dsa-mt7530-do-not-pass-port-variable-to-mt7531_r.patch
new file mode 100644 (file)
index 0000000..9ba12b1
--- /dev/null
@@ -0,0 +1,37 @@
+From c078ebbf5f6f6d8390035a9f92eeab766b78884d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:21 +0300
+Subject: [PATCH 14/15] net: dsa: mt7530: do not pass port variable to
+ mt7531_rgmii_setup()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The mt7531_rgmii_setup() function does not use the port variable, do not
+pass the variable to it.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2785,7 +2785,7 @@ mt7530_mac_config(struct dsa_switch *ds,
+               mt7530_setup_port6(priv->ds, interface);
+ }
+-static void mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port,
++static void mt7531_rgmii_setup(struct mt7530_priv *priv,
+                              phy_interface_t interface,
+                              struct phy_device *phydev)
+ {
+@@ -2836,7 +2836,7 @@ mt7531_mac_config(struct dsa_switch *ds,
+       if (phy_interface_mode_is_rgmii(interface)) {
+               dp = dsa_to_port(ds, port);
+               phydev = dp->slave->phydev;
+-              mt7531_rgmii_setup(priv, port, interface, phydev);
++              mt7531_rgmii_setup(priv, interface, phydev);
+       }
+ }
diff --git a/target/linux/generic/backport-6.6/790-52-v6.10-net-dsa-mt7530-explain-exposing-MDIO-bus-of-MT7531AE.patch b/target/linux/generic/backport-6.6/790-52-v6.10-net-dsa-mt7530-explain-exposing-MDIO-bus-of-MT7531AE.patch
new file mode 100644 (file)
index 0000000..58c3e0b
--- /dev/null
@@ -0,0 +1,33 @@
+From e7a9cc3cc00b40e0bc2bae40bd2ece0e48fa51d5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Mon, 22 Apr 2024 10:15:22 +0300
+Subject: [PATCH 15/15] net: dsa: mt7530: explain exposing MDIO bus of MT7531AE
+ better
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Unlike MT7531BE, the GPIO 6-12 pins are not used for RGMII on MT7531AE.
+Therefore, the GPIO 11-12 pins are set to function as MDC and MDIO to
+expose the MDIO bus of the switch. Replace the comment with a better
+explanation.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+---
+ drivers/net/dsa/mt7530.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2635,7 +2635,10 @@ mt7531_setup(struct dsa_switch *ds)
+       if (!priv->p5_sgmii) {
+               mt7531_pll_setup(priv);
+       } else {
+-              /* Let ds->slave_mii_bus be able to access external phy. */
++              /* Unlike MT7531BE, the GPIO 6-12 pins are not used for RGMII on
++               * MT7531AE. Set the GPIO 11-12 pins to function as MDC and MDIO
++               * to expose the MDIO bus of the switch.
++               */
+               mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO11_RG_RXD2_MASK,
+                          MT7531_EXT_P_MDC_11);
+               mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO12_RG_RXD3_MASK,
diff --git a/target/linux/generic/backport-6.6/790-53-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch b/target/linux/generic/backport-6.6/790-53-v6.10-net-dsa-mt7530-do-not-set-MT7530_P5_DIS-when-PHY-.patch
new file mode 100644 (file)
index 0000000..cee3d01
--- /dev/null
@@ -0,0 +1,45 @@
+From 16e6592cd5c5bd74d8890973489f60176c692614 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Sun, 28 Apr 2024 12:19:58 +0300
+Subject: [PATCH] net: dsa: mt7530: do not set MT7530_P5_DIS when PHY muxing is
+ being used
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+DSA initalises the ds->num_ports amount of ports in
+dsa_switch_touch_ports(). When the PHY muxing feature is in use, port 5
+won't be defined in the device tree. Because of this, the type member of
+the dsa_port structure for this port will be assigned DSA_PORT_TYPE_UNUSED.
+The dsa_port_setup() function calls ds->ops->port_disable() when the port
+type is DSA_PORT_TYPE_UNUSED.
+
+The MT7530_P5_DIS bit is unset in mt7530_setup() when PHY muxing is being
+used. mt7530_port_disable() which is assigned to ds->ops->port_disable() is
+called afterwards. Currently, mt7530_port_disable() sets MT7530_P5_DIS
+which breaks network connectivity when PHY muxing is being used.
+
+Therefore, do not set MT7530_P5_DIS when PHY muxing is being used.
+
+Fixes: 377174c5760c ("net: dsa: mt7530: move MT753X_MTRAP operations for MT7530")
+Reported-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240428-for-netnext-mt7530-do-not-disable-port5-when-phy-muxing-v2-1-bb7c37d293f8@arinc9.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1220,7 +1220,8 @@ mt7530_port_disable(struct dsa_switch *d
+       if (priv->id != ID_MT7530 && priv->id != ID_MT7621)
+               return;
+-      if (port == 5)
++      /* Do not set MT7530_P5_DIS when port 5 is being used for PHY muxing. */
++      if (port == 5 && priv->p5_mode == GMAC5)
+               mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS);
+       else if (port == 6)
+               mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS);
diff --git a/target/linux/generic/backport-6.6/790-54-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch b/target/linux/generic/backport-6.6/790-54-v6.10-796-net-dsa-mt7530-detect-PHY-muxing-when-PHY-is-defined.patch
new file mode 100644 (file)
index 0000000..d369c4e
--- /dev/null
@@ -0,0 +1,45 @@
+From d8dcf5bd6d0eace9f7c1daa14b63b3925b09d033 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= <arinc.unal@arinc9.com>
+Date: Tue, 30 Apr 2024 08:01:33 +0300
+Subject: [PATCH] net: dsa: mt7530: detect PHY muxing when PHY is defined on
+ switch MDIO bus
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently, the MT7530 DSA subdriver configures the MT7530 switch to provide
+direct access to switch PHYs, meaning, the switch PHYs listen on the MDIO
+bus the switch listens on. The PHY muxing feature makes use of this.
+
+This is problematic as the PHY may be attached before the switch is
+initialised, in which case, the PHY will fail to be attached.
+
+Since commit 91374ba537bd ("net: dsa: mt7530: support OF-based registration
+of switch MDIO bus"), we can describe the switch PHYs on the MDIO bus of
+the switch on the device tree. Extend the check to detect PHY muxing when
+the PHY is defined on the MDIO bus of the switch on the device tree.
+
+When the PHY is described this way, the switch will be initialised first,
+then the switch MDIO bus will be registered. Only after these steps, the
+PHY will be attached.
+
+Signed-off-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Reviewed-by: Daniel Golle <daniel@makrotopia.org>
+Link: https://lore.kernel.org/r/20240430-b4-for-netnext-mt7530-use-switch-mdio-bus-for-phy-muxing-v2-1-9104d886d0db@arinc9.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2484,7 +2484,8 @@ mt7530_setup(struct dsa_switch *ds)
+                       if (!phy_node)
+                               continue;
+-                      if (phy_node->parent == priv->dev->of_node->parent) {
++                      if (phy_node->parent == priv->dev->of_node->parent ||
++                          phy_node->parent->parent == priv->dev->of_node) {
+                               ret = of_get_phy_mode(mac_np, &interface);
+                               if (ret && ret != -ENODEV) {
+                                       of_node_put(mac_np);
diff --git a/target/linux/generic/backport-6.6/790-55-v6.11-net-dsa-mt7530-factor-out-bridge-join-leave-logic.patch b/target/linux/generic/backport-6.6/790-55-v6.11-net-dsa-mt7530-factor-out-bridge-join-leave-logic.patch
new file mode 100644 (file)
index 0000000..fdba021
--- /dev/null
@@ -0,0 +1,176 @@
+From c25c961fc7f36682f0a530150f1b7453ebc344cd Mon Sep 17 00:00:00 2001
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Tue, 18 Jun 2024 09:17:12 +0200
+Subject: [PATCH 1/2] net: dsa: mt7530: factor out bridge join/leave logic
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+As preparation for implementing bridge port isolation, move the logic to
+add and remove bits in the port matrix into a new helper
+mt7530_update_port_member(), which is called from
+mt7530_port_bridge_join() and mt7530_port_bridge_leave().
+
+Another part of the preparation is using dsa_port_offloads_bridge_dev()
+instead of dsa_port_offloads_bridge() to check for bridge membership, as
+we don't have a struct dsa_bridge in mt7530_port_bridge_flags().
+
+The port matrix setting is slightly streamlined, now always first setting
+the mt7530_port's pm field and then writing the port matrix from that
+field into the hardware register, instead of duplicating the bit
+manipulation for both the struct field and the register.
+
+mt7530_port_bridge_join() was previously using |= to update the port
+matrix with the port bitmap, which was unnecessary, as pm would only
+have the CPU port set before joining a bridge; a simple assignment can
+be used for both joining and leaving (and will also work when individual
+bits are added/removed in port_bitmap with regard to the previous port
+matrix, which is what happens with port isolation).
+
+No functional change intended.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Reviewed-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 105 ++++++++++++++++++---------------------
+ 1 file changed, 48 insertions(+), 57 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1302,6 +1302,52 @@ mt7530_stp_state_set(struct dsa_switch *
+                  FID_PST(FID_BRIDGED, stp_state));
+ }
++static void mt7530_update_port_member(struct mt7530_priv *priv, int port,
++                                    const struct net_device *bridge_dev,
++                                    bool join) __must_hold(&priv->reg_mutex)
++{
++      struct dsa_port *dp = dsa_to_port(priv->ds, port), *other_dp;
++      struct mt7530_port *p = &priv->ports[port], *other_p;
++      struct dsa_port *cpu_dp = dp->cpu_dp;
++      u32 port_bitmap = BIT(cpu_dp->index);
++      int other_port;
++
++      dsa_switch_for_each_user_port(other_dp, priv->ds) {
++              other_port = other_dp->index;
++              other_p = &priv->ports[other_port];
++
++              if (dp == other_dp)
++                      continue;
++
++              /* Add/remove this port to/from the port matrix of the other
++               * ports in the same bridge. If the port is disabled, port
++               * matrix is kept and not being setup until the port becomes
++               * enabled.
++               */
++              if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev))
++                      continue;
++
++              if (join) {
++                      other_p->pm |= PCR_MATRIX(BIT(port));
++                      port_bitmap |= BIT(other_port);
++              } else {
++                      other_p->pm &= ~PCR_MATRIX(BIT(port));
++              }
++
++              if (other_p->enable)
++                      mt7530_rmw(priv, MT7530_PCR_P(other_port),
++                                 PCR_MATRIX_MASK, other_p->pm);
++      }
++
++      /* Add/remove the all other ports to this port matrix. For !join
++       * (leaving the bridge), only the CPU port will remain in the port matrix
++       * of this port.
++       */
++      p->pm = PCR_MATRIX(port_bitmap);
++      if (priv->ports[port].enable)
++              mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, p->pm);
++}
++
+ static int
+ mt7530_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+                            struct switchdev_brport_flags flags,
+@@ -1345,39 +1391,11 @@ mt7530_port_bridge_join(struct dsa_switc
+                       struct dsa_bridge bridge, bool *tx_fwd_offload,
+                       struct netlink_ext_ack *extack)
+ {
+-      struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
+-      struct dsa_port *cpu_dp = dp->cpu_dp;
+-      u32 port_bitmap = BIT(cpu_dp->index);
+       struct mt7530_priv *priv = ds->priv;
+       mutex_lock(&priv->reg_mutex);
+-      dsa_switch_for_each_user_port(other_dp, ds) {
+-              int other_port = other_dp->index;
+-
+-              if (dp == other_dp)
+-                      continue;
+-
+-              /* Add this port to the port matrix of the other ports in the
+-               * same bridge. If the port is disabled, port matrix is kept
+-               * and not being setup until the port becomes enabled.
+-               */
+-              if (!dsa_port_offloads_bridge(other_dp, &bridge))
+-                      continue;
+-
+-              if (priv->ports[other_port].enable)
+-                      mt7530_set(priv, MT7530_PCR_P(other_port),
+-                                 PCR_MATRIX(BIT(port)));
+-              priv->ports[other_port].pm |= PCR_MATRIX(BIT(port));
+-
+-              port_bitmap |= BIT(other_port);
+-      }
+-
+-      /* Add the all other ports to this port matrix. */
+-      if (priv->ports[port].enable)
+-              mt7530_rmw(priv, MT7530_PCR_P(port),
+-                         PCR_MATRIX_MASK, PCR_MATRIX(port_bitmap));
+-      priv->ports[port].pm |= PCR_MATRIX(port_bitmap);
++      mt7530_update_port_member(priv, port, bridge.dev, true);
+       /* Set to fallback mode for independent VLAN learning */
+       mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
+@@ -1478,38 +1496,11 @@ static void
+ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
+                        struct dsa_bridge bridge)
+ {
+-      struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
+-      struct dsa_port *cpu_dp = dp->cpu_dp;
+       struct mt7530_priv *priv = ds->priv;
+       mutex_lock(&priv->reg_mutex);
+-      dsa_switch_for_each_user_port(other_dp, ds) {
+-              int other_port = other_dp->index;
+-
+-              if (dp == other_dp)
+-                      continue;
+-
+-              /* Remove this port from the port matrix of the other ports
+-               * in the same bridge. If the port is disabled, port matrix
+-               * is kept and not being setup until the port becomes enabled.
+-               */
+-              if (!dsa_port_offloads_bridge(other_dp, &bridge))
+-                      continue;
+-
+-              if (priv->ports[other_port].enable)
+-                      mt7530_clear(priv, MT7530_PCR_P(other_port),
+-                                   PCR_MATRIX(BIT(port)));
+-              priv->ports[other_port].pm &= ~PCR_MATRIX(BIT(port));
+-      }
+-
+-      /* Set the cpu port to be the only one in the port matrix of
+-       * this port.
+-       */
+-      if (priv->ports[port].enable)
+-              mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
+-                         PCR_MATRIX(BIT(cpu_dp->index)));
+-      priv->ports[port].pm = PCR_MATRIX(BIT(cpu_dp->index));
++      mt7530_update_port_member(priv, port, bridge.dev, false);
+       /* When a port is removed from the bridge, the port would be set up
+        * back to the default as is at initial boot which is a VLAN-unaware
diff --git a/target/linux/generic/backport-6.6/790-56-v6.11-net-dsa-mt7530-add-support-for-bridge-port-isolation.patch b/target/linux/generic/backport-6.6/790-56-v6.11-net-dsa-mt7530-add-support-for-bridge-port-isolation.patch
new file mode 100644 (file)
index 0000000..2eb7859
--- /dev/null
@@ -0,0 +1,79 @@
+From 3d49ee2127c26fd2c77944fd2e3168c057f99439 Mon Sep 17 00:00:00 2001
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Tue, 18 Jun 2024 09:17:13 +0200
+Subject: [PATCH 2/2] net: dsa: mt7530: add support for bridge port isolation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Remove a pair of ports from the port matrix when both ports have the
+isolated flag set.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Reviewed-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Tested-by: Arınç ÜNAL <arinc.unal@arinc9.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 18 ++++++++++++++++--
+ drivers/net/dsa/mt7530.h |  1 +
+ 2 files changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -1311,6 +1311,7 @@ static void mt7530_update_port_member(st
+       struct dsa_port *cpu_dp = dp->cpu_dp;
+       u32 port_bitmap = BIT(cpu_dp->index);
+       int other_port;
++      bool isolated;
+       dsa_switch_for_each_user_port(other_dp, priv->ds) {
+               other_port = other_dp->index;
+@@ -1327,7 +1328,9 @@ static void mt7530_update_port_member(st
+               if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev))
+                       continue;
+-              if (join) {
++              isolated = p->isolated && other_p->isolated;
++
++              if (join && !isolated) {
+                       other_p->pm |= PCR_MATRIX(BIT(port));
+                       port_bitmap |= BIT(other_port);
+               } else {
+@@ -1354,7 +1357,7 @@ mt7530_port_pre_bridge_flags(struct dsa_
+                            struct netlink_ext_ack *extack)
+ {
+       if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
+-                         BR_BCAST_FLOOD))
++                         BR_BCAST_FLOOD | BR_ISOLATED))
+               return -EINVAL;
+       return 0;
+@@ -1383,6 +1386,17 @@ mt7530_port_bridge_flags(struct dsa_swit
+               mt7530_rmw(priv, MT753X_MFC, BC_FFP(BIT(port)),
+                          flags.val & BR_BCAST_FLOOD ? BC_FFP(BIT(port)) : 0);
++      if (flags.mask & BR_ISOLATED) {
++              struct dsa_port *dp = dsa_to_port(ds, port);
++              struct net_device *bridge_dev = dsa_port_bridge_dev_get(dp);
++
++              priv->ports[port].isolated = !!(flags.val & BR_ISOLATED);
++
++              mutex_lock(&priv->reg_mutex);
++              mt7530_update_port_member(priv, port, bridge_dev, true);
++              mutex_unlock(&priv->reg_mutex);
++      }
++
+       return 0;
+ }
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -721,6 +721,7 @@ struct mt7530_fdb {
+  */
+ struct mt7530_port {
+       bool enable;
++      bool isolated;
+       u32 pm;
+       u16 pvid;
+       struct phylink_pcs *sgmii_pcs;
diff --git a/target/linux/generic/backport-6.6/791-v6.11-01-net-phy-aquantia-move-priv-and-hw-stat-to-header.patch b/target/linux/generic/backport-6.6/791-v6.11-01-net-phy-aquantia-move-priv-and-hw-stat-to-header.patch
new file mode 100644 (file)
index 0000000..62137c3
--- /dev/null
@@ -0,0 +1,122 @@
+From c11d5dbbe73fa7b450aaa77bb18df86a9714b422 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Sat, 1 Jun 2024 01:35:02 +0200
+Subject: [PATCH 1/2] net: phy: aquantia: move priv and hw stat to header
+
+In preparation for LEDs support, move priv and hw stat to header to
+reference priv struct also in other .c outside aquantia.main
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/aquantia/aquantia.h      | 38 ++++++++++++++++++++++++
+ drivers/net/phy/aquantia/aquantia_main.c | 37 -----------------------
+ 2 files changed, 38 insertions(+), 37 deletions(-)
+
+--- a/drivers/net/phy/aquantia/aquantia.h
++++ b/drivers/net/phy/aquantia/aquantia.h
+@@ -82,6 +82,18 @@
+ #define VEND1_GLOBAL_RSVD_STAT9_MODE          GENMASK(7, 0)
+ #define VEND1_GLOBAL_RSVD_STAT9_1000BT2               0x23
++/* MDIO_MMD_C22EXT */
++#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES         0xd292
++#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES          0xd294
++#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER               0xd297
++#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES         0xd313
++#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES          0xd315
++#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER               0xd317
++#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS          0xd318
++#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS     0xd319
++#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR     0xd31a
++#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES         0xd31b
++
+ #define VEND1_GLOBAL_INT_STD_STATUS           0xfc00
+ #define VEND1_GLOBAL_INT_VEND_STATUS          0xfc01
+@@ -108,6 +120,32 @@
+ #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2    BIT(1)
+ #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3    BIT(0)
++struct aqr107_hw_stat {
++      const char *name;
++      int reg;
++      int size;
++};
++
++#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s }
++static const struct aqr107_hw_stat aqr107_hw_stats[] = {
++      SGMII_STAT("sgmii_rx_good_frames",          RX_GOOD_FRAMES,     26),
++      SGMII_STAT("sgmii_rx_bad_frames",           RX_BAD_FRAMES,      26),
++      SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER,    8),
++      SGMII_STAT("sgmii_tx_good_frames",          TX_GOOD_FRAMES,     26),
++      SGMII_STAT("sgmii_tx_bad_frames",           TX_BAD_FRAMES,      26),
++      SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER,    8),
++      SGMII_STAT("sgmii_tx_collisions",           TX_COLLISIONS,       8),
++      SGMII_STAT("sgmii_tx_line_collisions",      TX_LINE_COLLISIONS,  8),
++      SGMII_STAT("sgmii_tx_frame_alignment_err",  TX_FRAME_ALIGN_ERR, 16),
++      SGMII_STAT("sgmii_tx_runt_frames",          TX_RUNT_FRAMES,     22),
++};
++
++#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
++
++struct aqr107_priv {
++      u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
++};
++
+ #if IS_REACHABLE(CONFIG_HWMON)
+ int aqr_hwmon_probe(struct phy_device *phydev);
+ #else
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -84,49 +84,12 @@
+ #define MDIO_AN_RX_VEND_STAT3                 0xe832
+ #define MDIO_AN_RX_VEND_STAT3_AFR             BIT(0)
+-/* MDIO_MMD_C22EXT */
+-#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES         0xd292
+-#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES          0xd294
+-#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER               0xd297
+-#define MDIO_C22EXT_STAT_SGMII_TX_GOOD_FRAMES         0xd313
+-#define MDIO_C22EXT_STAT_SGMII_TX_BAD_FRAMES          0xd315
+-#define MDIO_C22EXT_STAT_SGMII_TX_FALSE_CARRIER               0xd317
+-#define MDIO_C22EXT_STAT_SGMII_TX_COLLISIONS          0xd318
+-#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS     0xd319
+-#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR     0xd31a
+-#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES         0xd31b
+-
+ /* Sleep and timeout for checking if the Processor-Intensive
+  * MDIO operation is finished
+  */
+ #define AQR107_OP_IN_PROG_SLEEP               1000
+ #define AQR107_OP_IN_PROG_TIMEOUT     100000
+-struct aqr107_hw_stat {
+-      const char *name;
+-      int reg;
+-      int size;
+-};
+-
+-#define SGMII_STAT(n, r, s) { n, MDIO_C22EXT_STAT_SGMII_ ## r, s }
+-static const struct aqr107_hw_stat aqr107_hw_stats[] = {
+-      SGMII_STAT("sgmii_rx_good_frames",          RX_GOOD_FRAMES,     26),
+-      SGMII_STAT("sgmii_rx_bad_frames",           RX_BAD_FRAMES,      26),
+-      SGMII_STAT("sgmii_rx_false_carrier_events", RX_FALSE_CARRIER,    8),
+-      SGMII_STAT("sgmii_tx_good_frames",          TX_GOOD_FRAMES,     26),
+-      SGMII_STAT("sgmii_tx_bad_frames",           TX_BAD_FRAMES,      26),
+-      SGMII_STAT("sgmii_tx_false_carrier_events", TX_FALSE_CARRIER,    8),
+-      SGMII_STAT("sgmii_tx_collisions",           TX_COLLISIONS,       8),
+-      SGMII_STAT("sgmii_tx_line_collisions",      TX_LINE_COLLISIONS,  8),
+-      SGMII_STAT("sgmii_tx_frame_alignment_err",  TX_FRAME_ALIGN_ERR, 16),
+-      SGMII_STAT("sgmii_tx_runt_frames",          TX_RUNT_FRAMES,     22),
+-};
+-#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)
+-
+-struct aqr107_priv {
+-      u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
+-};
+-
+ static int aqr107_get_sset_count(struct phy_device *phydev)
+ {
+       return AQR107_SGMII_STAT_SZ;
diff --git a/target/linux/generic/backport-6.6/791-v6.11-02-net-phy-aquantia-add-support-for-PHY-LEDs.patch b/target/linux/generic/backport-6.6/791-v6.11-02-net-phy-aquantia-add-support-for-PHY-LEDs.patch
new file mode 100644 (file)
index 0000000..dcbe62c
--- /dev/null
@@ -0,0 +1,395 @@
+From 61578f67937881abf54c8bd258eb913312dbe4c1 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 1 Jun 2024 01:35:03 +0200
+Subject: [PATCH 2/2] net: phy: aquantia: add support for PHY LEDs
+
+Aquantia Ethernet PHYs got 3 LED output pins which are typically used
+to indicate link status and activity.
+Add a minimal LED controller driver supporting the most common uses
+with the 'netdev' trigger as well as software-driven forced control of
+the LEDs.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+[ rework indentation, fix checkpatch error and improve some functions ]
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/aquantia/Makefile        |   2 +-
+ drivers/net/phy/aquantia/aquantia.h      |  40 ++++++
+ drivers/net/phy/aquantia/aquantia_leds.c | 150 +++++++++++++++++++++++
+ drivers/net/phy/aquantia/aquantia_main.c |  63 +++++++++-
+ 4 files changed, 252 insertions(+), 3 deletions(-)
+ create mode 100644 drivers/net/phy/aquantia/aquantia_leds.c
+
+--- a/drivers/net/phy/aquantia/Makefile
++++ b/drivers/net/phy/aquantia/Makefile
+@@ -1,5 +1,5 @@
+ # SPDX-License-Identifier: GPL-2.0
+-aquantia-objs                 += aquantia_main.o aquantia_firmware.o
++aquantia-objs                 += aquantia_main.o aquantia_firmware.o aquantia_leds.o
+ ifdef CONFIG_HWMON
+ aquantia-objs                 += aquantia_hwmon.o
+ endif
+--- a/drivers/net/phy/aquantia/aquantia.h
++++ b/drivers/net/phy/aquantia/aquantia.h
+@@ -58,6 +58,28 @@
+ #define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD        BIT(6)
+ #define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL    BIT(0)
++#define VEND1_GLOBAL_LED_PROV                 0xc430
++#define AQR_LED_PROV(x)                               (VEND1_GLOBAL_LED_PROV + (x))
++#define VEND1_GLOBAL_LED_PROV_LINK2500                BIT(14)
++#define VEND1_GLOBAL_LED_PROV_LINK5000                BIT(15)
++#define VEND1_GLOBAL_LED_PROV_FORCE_ON                BIT(8)
++#define VEND1_GLOBAL_LED_PROV_LINK10000               BIT(7)
++#define VEND1_GLOBAL_LED_PROV_LINK1000                BIT(6)
++#define VEND1_GLOBAL_LED_PROV_LINK100         BIT(5)
++#define VEND1_GLOBAL_LED_PROV_RX_ACT          BIT(3)
++#define VEND1_GLOBAL_LED_PROV_TX_ACT          BIT(2)
++#define VEND1_GLOBAL_LED_PROV_ACT_STRETCH     GENMASK(0, 1)
++
++#define VEND1_GLOBAL_LED_PROV_LINK_MASK               (VEND1_GLOBAL_LED_PROV_LINK100 | \
++                                               VEND1_GLOBAL_LED_PROV_LINK1000 | \
++                                               VEND1_GLOBAL_LED_PROV_LINK10000 | \
++                                               VEND1_GLOBAL_LED_PROV_LINK5000 | \
++                                               VEND1_GLOBAL_LED_PROV_LINK2500)
++
++#define VEND1_GLOBAL_LED_DRIVE                        0xc438
++#define VEND1_GLOBAL_LED_DRIVE_VDD            BIT(1)
++#define AQR_LED_DRIVE(x)                      (VEND1_GLOBAL_LED_DRIVE + (x))
++
+ #define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL     0xc421
+ #define VEND1_THERMAL_PROV_LOW_TEMP_FAIL      0xc422
+ #define VEND1_THERMAL_PROV_HIGH_TEMP_WARN     0xc423
+@@ -120,6 +142,8 @@
+ #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2    BIT(1)
+ #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3    BIT(0)
++#define AQR_MAX_LEDS                          3
++
+ struct aqr107_hw_stat {
+       const char *name;
+       int reg;
+@@ -144,6 +168,7 @@ static const struct aqr107_hw_stat aqr10
+ struct aqr107_priv {
+       u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
++      unsigned long leds_active_low;
+ };
+ #if IS_REACHABLE(CONFIG_HWMON)
+@@ -153,3 +178,18 @@ static inline int aqr_hwmon_probe(struct
+ #endif
+ int aqr_firmware_load(struct phy_device *phydev);
++
++int aqr_phy_led_blink_set(struct phy_device *phydev, u8 index,
++                        unsigned long *delay_on,
++                        unsigned long *delay_off);
++int aqr_phy_led_brightness_set(struct phy_device *phydev,
++                             u8 index, enum led_brightness value);
++int aqr_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                              unsigned long rules);
++int aqr_phy_led_hw_control_get(struct phy_device *phydev, u8 index,
++                             unsigned long *rules);
++int aqr_phy_led_hw_control_set(struct phy_device *phydev, u8 index,
++                             unsigned long rules);
++int aqr_phy_led_active_low_set(struct phy_device *phydev, int index, bool enable);
++int aqr_phy_led_polarity_set(struct phy_device *phydev, int index,
++                           unsigned long modes);
+--- /dev/null
++++ b/drivers/net/phy/aquantia/aquantia_leds.c
+@@ -0,0 +1,150 @@
++// SPDX-License-Identifier: GPL-2.0
++/* LED driver for Aquantia PHY
++ *
++ * Author: Daniel Golle <daniel@makrotopia.org>
++ */
++
++#include <linux/phy.h>
++
++#include "aquantia.h"
++
++int aqr_phy_led_brightness_set(struct phy_device *phydev,
++                             u8 index, enum led_brightness value)
++{
++      if (index >= AQR_MAX_LEDS)
++              return -EINVAL;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index),
++                            VEND1_GLOBAL_LED_PROV_LINK_MASK |
++                            VEND1_GLOBAL_LED_PROV_FORCE_ON |
++                            VEND1_GLOBAL_LED_PROV_RX_ACT |
++                            VEND1_GLOBAL_LED_PROV_TX_ACT,
++                            value ? VEND1_GLOBAL_LED_PROV_FORCE_ON : 0);
++}
++
++static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
++                                               BIT(TRIGGER_NETDEV_LINK_100) |
++                                               BIT(TRIGGER_NETDEV_LINK_1000) |
++                                               BIT(TRIGGER_NETDEV_LINK_2500) |
++                                               BIT(TRIGGER_NETDEV_LINK_5000) |
++                                               BIT(TRIGGER_NETDEV_LINK_10000)  |
++                                               BIT(TRIGGER_NETDEV_RX) |
++                                               BIT(TRIGGER_NETDEV_TX));
++
++int aqr_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                              unsigned long rules)
++{
++      if (index >= AQR_MAX_LEDS)
++              return -EINVAL;
++
++      /* All combinations of the supported triggers are allowed */
++      if (rules & ~supported_triggers)
++              return -EOPNOTSUPP;
++
++      return 0;
++}
++
++int aqr_phy_led_hw_control_get(struct phy_device *phydev, u8 index,
++                             unsigned long *rules)
++{
++      int val;
++
++      if (index >= AQR_MAX_LEDS)
++              return -EINVAL;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index));
++      if (val < 0)
++              return val;
++
++      *rules = 0;
++      if (val & VEND1_GLOBAL_LED_PROV_LINK100)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_100);
++
++      if (val & VEND1_GLOBAL_LED_PROV_LINK1000)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
++
++      if (val & VEND1_GLOBAL_LED_PROV_LINK2500)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_2500);
++
++      if (val & VEND1_GLOBAL_LED_PROV_LINK5000)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_5000);
++
++      if (val & VEND1_GLOBAL_LED_PROV_LINK10000)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_10000);
++
++      if (val & VEND1_GLOBAL_LED_PROV_RX_ACT)
++              *rules |= BIT(TRIGGER_NETDEV_RX);
++
++      if (val & VEND1_GLOBAL_LED_PROV_TX_ACT)
++              *rules |= BIT(TRIGGER_NETDEV_TX);
++
++      return 0;
++}
++
++int aqr_phy_led_hw_control_set(struct phy_device *phydev, u8 index,
++                             unsigned long rules)
++{
++      u16 val = 0;
++
++      if (index >= AQR_MAX_LEDS)
++              return -EINVAL;
++
++      if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
++              val |= VEND1_GLOBAL_LED_PROV_LINK100;
++
++      if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
++              val |= VEND1_GLOBAL_LED_PROV_LINK1000;
++
++      if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
++              val |= VEND1_GLOBAL_LED_PROV_LINK2500;
++
++      if (rules & (BIT(TRIGGER_NETDEV_LINK_5000) | BIT(TRIGGER_NETDEV_LINK)))
++              val |= VEND1_GLOBAL_LED_PROV_LINK5000;
++
++      if (rules & (BIT(TRIGGER_NETDEV_LINK_10000) | BIT(TRIGGER_NETDEV_LINK)))
++              val |= VEND1_GLOBAL_LED_PROV_LINK10000;
++
++      if (rules & BIT(TRIGGER_NETDEV_RX))
++              val |= VEND1_GLOBAL_LED_PROV_RX_ACT;
++
++      if (rules & BIT(TRIGGER_NETDEV_TX))
++              val |= VEND1_GLOBAL_LED_PROV_TX_ACT;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index),
++                            VEND1_GLOBAL_LED_PROV_LINK_MASK |
++                            VEND1_GLOBAL_LED_PROV_FORCE_ON |
++                            VEND1_GLOBAL_LED_PROV_RX_ACT |
++                            VEND1_GLOBAL_LED_PROV_TX_ACT, val);
++}
++
++int aqr_phy_led_active_low_set(struct phy_device *phydev, int index, bool enable)
++{
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index),
++                            VEND1_GLOBAL_LED_DRIVE_VDD, enable);
++}
++
++int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes)
++{
++      struct aqr107_priv *priv = phydev->priv;
++      bool active_low = false;
++      u32 mode;
++
++      if (index >= AQR_MAX_LEDS)
++              return -EINVAL;
++
++      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++              switch (mode) {
++              case PHY_LED_ACTIVE_LOW:
++                      active_low = true;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      /* Save LED driver vdd state to restore on SW reset */
++      if (active_low)
++              priv->leds_active_low |= BIT(index);
++
++      return aqr_phy_led_active_low_set(phydev, index, active_low);
++}
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -475,7 +475,9 @@ static void aqr107_chip_info(struct phy_
+ static int aqr107_config_init(struct phy_device *phydev)
+ {
+-      int ret;
++      struct aqr107_priv *priv = phydev->priv;
++      u32 led_active_low;
++      int ret, index = 0;
+       /* Check that the PHY interface type is compatible */
+       if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
+@@ -496,7 +498,19 @@ static int aqr107_config_init(struct phy
+       if (!ret)
+               aqr107_chip_info(phydev);
+-      return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
++      ret = aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
++      if (ret)
++              return ret;
++
++      /* Restore LED polarity state after reset */
++      for_each_set_bit(led_active_low, &priv->leds_active_low, AQR_MAX_LEDS) {
++              ret = aqr_phy_led_active_low_set(phydev, index, led_active_low);
++              if (ret)
++                      return ret;
++              index++;
++      }
++
++      return 0;
+ }
+ static int aqcs109_config_init(struct phy_device *phydev)
+@@ -703,6 +717,11 @@ static struct phy_driver aqr_driver[] =
+       .get_strings    = aqr107_get_strings,
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
++      .led_brightness_set = aqr_phy_led_brightness_set,
++      .led_hw_is_supported = aqr_phy_led_hw_is_supported,
++      .led_hw_control_set = aqr_phy_led_hw_control_set,
++      .led_hw_control_get = aqr_phy_led_hw_control_get,
++      .led_polarity_set = aqr_phy_led_polarity_set,
+ },
+ {
+       PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
+@@ -722,6 +741,11 @@ static struct phy_driver aqr_driver[] =
+       .get_strings    = aqr107_get_strings,
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
++      .led_brightness_set = aqr_phy_led_brightness_set,
++      .led_hw_is_supported = aqr_phy_led_hw_is_supported,
++      .led_hw_control_set = aqr_phy_led_hw_control_set,
++      .led_hw_control_get = aqr_phy_led_hw_control_get,
++      .led_polarity_set = aqr_phy_led_polarity_set,
+ },
+ {
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR111),
+@@ -741,6 +765,11 @@ static struct phy_driver aqr_driver[] =
+       .get_strings    = aqr107_get_strings,
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
++      .led_brightness_set = aqr_phy_led_brightness_set,
++      .led_hw_is_supported = aqr_phy_led_hw_is_supported,
++      .led_hw_control_set = aqr_phy_led_hw_control_set,
++      .led_hw_control_get = aqr_phy_led_hw_control_get,
++      .led_polarity_set = aqr_phy_led_polarity_set,
+ },
+ {
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0),
+@@ -760,6 +789,11 @@ static struct phy_driver aqr_driver[] =
+       .get_strings    = aqr107_get_strings,
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
++      .led_brightness_set = aqr_phy_led_brightness_set,
++      .led_hw_is_supported = aqr_phy_led_hw_is_supported,
++      .led_hw_control_set = aqr_phy_led_hw_control_set,
++      .led_hw_control_get = aqr_phy_led_hw_control_get,
++      .led_polarity_set = aqr_phy_led_polarity_set,
+ },
+ {
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR405),
+@@ -786,6 +820,11 @@ static struct phy_driver aqr_driver[] =
+       .get_strings    = aqr107_get_strings,
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
++      .led_brightness_set = aqr_phy_led_brightness_set,
++      .led_hw_is_supported = aqr_phy_led_hw_is_supported,
++      .led_hw_control_set = aqr_phy_led_hw_control_set,
++      .led_hw_control_get = aqr_phy_led_hw_control_get,
++      .led_polarity_set = aqr_phy_led_polarity_set,
+ },
+ {
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
+@@ -823,6 +862,11 @@ static struct phy_driver aqr_driver[] =
+       .get_strings    = aqr107_get_strings,
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
++      .led_brightness_set = aqr_phy_led_brightness_set,
++      .led_hw_is_supported = aqr_phy_led_hw_is_supported,
++      .led_hw_control_set = aqr_phy_led_hw_control_set,
++      .led_hw_control_get = aqr_phy_led_hw_control_get,
++      .led_polarity_set = aqr_phy_led_polarity_set,
+ },
+ {
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
+@@ -842,6 +886,11 @@ static struct phy_driver aqr_driver[] =
+       .get_strings    = aqr107_get_strings,
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
++      .led_brightness_set = aqr_phy_led_brightness_set,
++      .led_hw_is_supported = aqr_phy_led_hw_is_supported,
++      .led_hw_control_set = aqr_phy_led_hw_control_set,
++      .led_hw_control_get = aqr_phy_led_hw_control_get,
++      .led_polarity_set = aqr_phy_led_polarity_set,
+ },
+ {
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR114C),
+@@ -861,6 +910,11 @@ static struct phy_driver aqr_driver[] =
+       .get_strings    = aqr107_get_strings,
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
++      .led_brightness_set = aqr_phy_led_brightness_set,
++      .led_hw_is_supported = aqr_phy_led_hw_is_supported,
++      .led_hw_control_set = aqr_phy_led_hw_control_set,
++      .led_hw_control_get = aqr_phy_led_hw_control_get,
++      .led_polarity_set = aqr_phy_led_polarity_set,
+ },
+ {
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
+@@ -880,6 +934,11 @@ static struct phy_driver aqr_driver[] =
+       .get_strings    = aqr107_get_strings,
+       .get_stats      = aqr107_get_stats,
+       .link_change_notify = aqr107_link_change_notify,
++      .led_brightness_set = aqr_phy_led_brightness_set,
++      .led_hw_is_supported = aqr_phy_led_hw_is_supported,
++      .led_hw_control_set = aqr_phy_led_hw_control_set,
++      .led_hw_control_get = aqr_phy_led_hw_control_get,
++      .led_polarity_set = aqr_phy_led_polarity_set,
+ },
+ };
diff --git a/target/linux/generic/backport-6.6/795-v6.7-16-r8152-use-napi_gro_frags.patch b/target/linux/generic/backport-6.6/795-v6.7-16-r8152-use-napi_gro_frags.patch
new file mode 100644 (file)
index 0000000..85b320f
--- /dev/null
@@ -0,0 +1,122 @@
+From 788d30daa8f97f06166b6a63f0e51f2a4c2f036a Mon Sep 17 00:00:00 2001
+From: Hayes Wang <hayeswang@realtek.com>
+Date: Tue, 26 Sep 2023 19:17:14 +0800
+Subject: [PATCH] r8152: use napi_gro_frags
+
+Use napi_gro_frags() for the skb of fragments when the work_done is less
+than budget.
+
+Signed-off-by: Hayes Wang <hayeswang@realtek.com>
+Link: https://lore.kernel.org/r/20230926111714.9448-434-nic_swsd@realtek.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/usb/r8152.c | 67 ++++++++++++++++++++++++++++++-----------
+ 1 file changed, 50 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/usb/r8152.c
++++ b/drivers/net/usb/r8152.c
+@@ -2584,8 +2584,9 @@ static int rx_bottom(struct r8152 *tp, i
+               while (urb->actual_length > len_used) {
+                       struct net_device *netdev = tp->netdev;
+                       struct net_device_stats *stats = &netdev->stats;
+-                      unsigned int pkt_len, rx_frag_head_sz;
++                      unsigned int pkt_len, rx_frag_head_sz, len;
+                       struct sk_buff *skb;
++                      bool use_frags;
+                       WARN_ON_ONCE(skb_queue_len(&tp->rx_queue) >= 1000);
+@@ -2598,45 +2599,77 @@ static int rx_bottom(struct r8152 *tp, i
+                               break;
+                       pkt_len -= ETH_FCS_LEN;
++                      len = pkt_len;
+                       rx_data += sizeof(struct rx_desc);
+-                      if (!agg_free || tp->rx_copybreak > pkt_len)
+-                              rx_frag_head_sz = pkt_len;
++                      if (!agg_free || tp->rx_copybreak > len)
++                              use_frags = false;
+                       else
+-                              rx_frag_head_sz = tp->rx_copybreak;
++                              use_frags = true;
++
++                      if (use_frags) {
++                              /* If the budget is exhausted, the packet
++                               * would be queued in the driver. That is,
++                               * napi_gro_frags() wouldn't be called, so
++                               * we couldn't use napi_get_frags().
++                               */
++                              if (work_done >= budget) {
++                                      rx_frag_head_sz = tp->rx_copybreak;
++                                      skb = napi_alloc_skb(napi,
++                                                           rx_frag_head_sz);
++                              } else {
++                                      rx_frag_head_sz = 0;
++                                      skb = napi_get_frags(napi);
++                              }
++                      } else {
++                              rx_frag_head_sz = 0;
++                              skb = napi_alloc_skb(napi, len);
++                      }
+-                      skb = napi_alloc_skb(napi, rx_frag_head_sz);
+                       if (!skb) {
+                               stats->rx_dropped++;
+                               goto find_next_rx;
+                       }
+                       skb->ip_summed = r8152_rx_csum(tp, rx_desc);
+-                      memcpy(skb->data, rx_data, rx_frag_head_sz);
+-                      skb_put(skb, rx_frag_head_sz);
+-                      pkt_len -= rx_frag_head_sz;
+-                      rx_data += rx_frag_head_sz;
+-                      if (pkt_len) {
++                      rtl_rx_vlan_tag(rx_desc, skb);
++
++                      if (use_frags) {
++                              if (rx_frag_head_sz) {
++                                      memcpy(skb->data, rx_data,
++                                             rx_frag_head_sz);
++                                      skb_put(skb, rx_frag_head_sz);
++                                      len -= rx_frag_head_sz;
++                                      rx_data += rx_frag_head_sz;
++                                      skb->protocol = eth_type_trans(skb,
++                                                                     netdev);
++                              }
++
+                               skb_add_rx_frag(skb, 0, agg->page,
+                                               agg_offset(agg, rx_data),
+-                                              pkt_len,
+-                                              SKB_DATA_ALIGN(pkt_len));
++                                              len, SKB_DATA_ALIGN(len));
+                               get_page(agg->page);
++                      } else {
++                              memcpy(skb->data, rx_data, len);
++                              skb_put(skb, len);
++                              skb->protocol = eth_type_trans(skb, netdev);
+                       }
+-                      skb->protocol = eth_type_trans(skb, netdev);
+-                      rtl_rx_vlan_tag(rx_desc, skb);
+                       if (work_done < budget) {
++                              if (use_frags)
++                                      napi_gro_frags(napi);
++                              else
++                                      napi_gro_receive(napi, skb);
++
+                               work_done++;
+                               stats->rx_packets++;
+-                              stats->rx_bytes += skb->len;
+-                              napi_gro_receive(napi, skb);
++                              stats->rx_bytes += pkt_len;
+                       } else {
+                               __skb_queue_tail(&tp->rx_queue, skb);
+                       }
+ find_next_rx:
+-                      rx_data = rx_agg_align(rx_data + pkt_len + ETH_FCS_LEN);
++                      rx_data = rx_agg_align(rx_data + len + ETH_FCS_LEN);
+                       rx_desc = (struct rx_desc *)rx_data;
+                       len_used = agg_offset(agg, rx_data);
+                       len_used += sizeof(struct rx_desc);
diff --git a/target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch b/target/linux/generic/backport-6.6/798-v6.10-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch
new file mode 100644 (file)
index 0000000..5b627cf
--- /dev/null
@@ -0,0 +1,1140 @@
+From 71e79430117d56c409c5ea485a263bc0d8083390 Mon Sep 17 00:00:00 2001
+From: Eric Woudstra <ericwouds@gmail.com>
+Date: Tue, 26 Mar 2024 17:23:05 +0100
+Subject: [PATCH] net: phy: air_en8811h: Add the Airoha EN8811H PHY driver
+
+Add the driver for the Airoha EN8811H 2.5 Gigabit PHY. The phy supports
+100/1000/2500 Mbps with auto negotiation only.
+
+The driver uses two firmware files, for which updated versions are added to
+linux-firmware already.
+
+Note: At phy-address + 8 there is another device on the mdio bus, that
+belongs to the EN881H. While the original driver writes to it, Airoha
+has confirmed this is not needed. Therefore, communication with this
+device is not included in this driver.
+
+Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240326162305.303598-3-ericwouds@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/Kconfig       |    5 +
+ drivers/net/phy/Makefile      |    1 +
+ drivers/net/phy/air_en8811h.c | 1086 +++++++++++++++++++++++++++++++++
+ 3 files changed, 1092 insertions(+)
+ create mode 100644 drivers/net/phy/air_en8811h.c
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -68,6 +68,11 @@ config SFP
+ comment "MII PHY device drivers"
++config AIR_EN8811H_PHY
++      tristate "Airoha EN8811H 2.5 Gigabit PHY"
++      help
++        Currently supports the Airoha EN8811H PHY.
++
+ config AMD_PHY
+       tristate "AMD PHYs"
+       help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -34,6 +34,7 @@ obj-y                                += $(sfp-obj-y) $(sfp-obj-m)
+ obj-$(CONFIG_ADIN_PHY)                += adin.o
+ obj-$(CONFIG_ADIN1100_PHY)    += adin1100.o
++obj-$(CONFIG_AIR_EN8811H_PHY)   += air_en8811h.o
+ obj-$(CONFIG_AMD_PHY)         += amd.o
+ obj-$(CONFIG_AQUANTIA_PHY)    += aquantia/
+ obj-$(CONFIG_AX88796B_PHY)    += ax88796b.o
+--- /dev/null
++++ b/drivers/net/phy/air_en8811h.c
+@@ -0,0 +1,1086 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Driver for the Airoha EN8811H 2.5 Gigabit PHY.
++ *
++ * Limitations of the EN8811H:
++ * - Only full duplex supported
++ * - Forced speed (AN off) is not supported by hardware (100Mbps)
++ *
++ * Source originated from airoha's en8811h.c and en8811h.h v1.2.1
++ *
++ * Copyright (C) 2023 Airoha Technology Corp.
++ */
++
++#include <linux/phy.h>
++#include <linux/firmware.h>
++#include <linux/property.h>
++#include <linux/wordpart.h>
++#include <asm/unaligned.h>
++
++#define EN8811H_PHY_ID                0x03a2a411
++
++#define EN8811H_MD32_DM               "airoha/EthMD32.dm.bin"
++#define EN8811H_MD32_DSP      "airoha/EthMD32.DSP.bin"
++
++#define AIR_FW_ADDR_DM        0x00000000
++#define AIR_FW_ADDR_DSP       0x00100000
++
++/* MII Registers */
++#define AIR_AUX_CTRL_STATUS           0x1d
++#define   AIR_AUX_CTRL_STATUS_SPEED_MASK      GENMASK(4, 2)
++#define   AIR_AUX_CTRL_STATUS_SPEED_100               0x4
++#define   AIR_AUX_CTRL_STATUS_SPEED_1000      0x8
++#define   AIR_AUX_CTRL_STATUS_SPEED_2500      0xc
++
++#define AIR_EXT_PAGE_ACCESS           0x1f
++#define   AIR_PHY_PAGE_STANDARD                       0x0000
++#define   AIR_PHY_PAGE_EXTENDED_4             0x0004
++
++/* MII Registers Page 4*/
++#define AIR_BPBUS_MODE                        0x10
++#define   AIR_BPBUS_MODE_ADDR_FIXED           0x0000
++#define   AIR_BPBUS_MODE_ADDR_INCR            BIT(15)
++#define AIR_BPBUS_WR_ADDR_HIGH                0x11
++#define AIR_BPBUS_WR_ADDR_LOW         0x12
++#define AIR_BPBUS_WR_DATA_HIGH                0x13
++#define AIR_BPBUS_WR_DATA_LOW         0x14
++#define AIR_BPBUS_RD_ADDR_HIGH                0x15
++#define AIR_BPBUS_RD_ADDR_LOW         0x16
++#define AIR_BPBUS_RD_DATA_HIGH                0x17
++#define AIR_BPBUS_RD_DATA_LOW         0x18
++
++/* Registers on MDIO_MMD_VEND1 */
++#define EN8811H_PHY_FW_STATUS         0x8009
++#define   EN8811H_PHY_READY                   0x02
++
++#define AIR_PHY_MCU_CMD_1             0x800c
++#define AIR_PHY_MCU_CMD_1_MODE1                       0x0
++#define AIR_PHY_MCU_CMD_2             0x800d
++#define AIR_PHY_MCU_CMD_2_MODE1                       0x0
++#define AIR_PHY_MCU_CMD_3             0x800e
++#define AIR_PHY_MCU_CMD_3_MODE1                       0x1101
++#define AIR_PHY_MCU_CMD_3_DOCMD                       0x1100
++#define AIR_PHY_MCU_CMD_4             0x800f
++#define AIR_PHY_MCU_CMD_4_MODE1                       0x0002
++#define AIR_PHY_MCU_CMD_4_INTCLR              0x00e4
++
++/* Registers on MDIO_MMD_VEND2 */
++#define AIR_PHY_LED_BCR                       0x021
++#define   AIR_PHY_LED_BCR_MODE_MASK           GENMASK(1, 0)
++#define   AIR_PHY_LED_BCR_TIME_TEST           BIT(2)
++#define   AIR_PHY_LED_BCR_CLK_EN              BIT(3)
++#define   AIR_PHY_LED_BCR_EXT_CTRL            BIT(15)
++
++#define AIR_PHY_LED_DUR_ON            0x022
++
++#define AIR_PHY_LED_DUR_BLINK         0x023
++
++#define AIR_PHY_LED_ON(i)            (0x024 + ((i) * 2))
++#define   AIR_PHY_LED_ON_MASK                 (GENMASK(6, 0) | BIT(8))
++#define   AIR_PHY_LED_ON_LINK1000             BIT(0)
++#define   AIR_PHY_LED_ON_LINK100              BIT(1)
++#define   AIR_PHY_LED_ON_LINK10                       BIT(2)
++#define   AIR_PHY_LED_ON_LINKDOWN             BIT(3)
++#define   AIR_PHY_LED_ON_FDX                  BIT(4) /* Full duplex */
++#define   AIR_PHY_LED_ON_HDX                  BIT(5) /* Half duplex */
++#define   AIR_PHY_LED_ON_FORCE_ON             BIT(6)
++#define   AIR_PHY_LED_ON_LINK2500             BIT(8)
++#define   AIR_PHY_LED_ON_POLARITY             BIT(14)
++#define   AIR_PHY_LED_ON_ENABLE                       BIT(15)
++
++#define AIR_PHY_LED_BLINK(i)         (0x025 + ((i) * 2))
++#define   AIR_PHY_LED_BLINK_1000TX            BIT(0)
++#define   AIR_PHY_LED_BLINK_1000RX            BIT(1)
++#define   AIR_PHY_LED_BLINK_100TX             BIT(2)
++#define   AIR_PHY_LED_BLINK_100RX             BIT(3)
++#define   AIR_PHY_LED_BLINK_10TX              BIT(4)
++#define   AIR_PHY_LED_BLINK_10RX              BIT(5)
++#define   AIR_PHY_LED_BLINK_COLLISION         BIT(6)
++#define   AIR_PHY_LED_BLINK_RX_CRC_ERR                BIT(7)
++#define   AIR_PHY_LED_BLINK_RX_IDLE_ERR               BIT(8)
++#define   AIR_PHY_LED_BLINK_FORCE_BLINK               BIT(9)
++#define   AIR_PHY_LED_BLINK_2500TX            BIT(10)
++#define   AIR_PHY_LED_BLINK_2500RX            BIT(11)
++
++/* Registers on BUCKPBUS */
++#define EN8811H_2P5G_LPA              0x3b30
++#define   EN8811H_2P5G_LPA_2P5G                       BIT(0)
++
++#define EN8811H_FW_VERSION            0x3b3c
++
++#define EN8811H_POLARITY              0xca0f8
++#define   EN8811H_POLARITY_TX_NORMAL          BIT(0)
++#define   EN8811H_POLARITY_RX_REVERSE         BIT(1)
++
++#define EN8811H_GPIO_OUTPUT           0xcf8b8
++#define   EN8811H_GPIO_OUTPUT_345             (BIT(3) | BIT(4) | BIT(5))
++
++#define EN8811H_FW_CTRL_1             0x0f0018
++#define   EN8811H_FW_CTRL_1_START             0x0
++#define   EN8811H_FW_CTRL_1_FINISH            0x1
++#define EN8811H_FW_CTRL_2             0x800000
++#define EN8811H_FW_CTRL_2_LOADING             BIT(11)
++
++/* Led definitions */
++#define EN8811H_LED_COUNT     3
++
++/* Default LED setup:
++ * GPIO5 <-> LED0  On: Link detected, blink Rx/Tx
++ * GPIO4 <-> LED1  On: Link detected at 2500 or 1000 Mbps
++ * GPIO3 <-> LED2  On: Link detected at 2500 or  100 Mbps
++ */
++#define AIR_DEFAULT_TRIGGER_LED0 (BIT(TRIGGER_NETDEV_LINK)      | \
++                                BIT(TRIGGER_NETDEV_RX)        | \
++                                BIT(TRIGGER_NETDEV_TX))
++#define AIR_DEFAULT_TRIGGER_LED1 (BIT(TRIGGER_NETDEV_LINK_2500) | \
++                                BIT(TRIGGER_NETDEV_LINK_1000))
++#define AIR_DEFAULT_TRIGGER_LED2 (BIT(TRIGGER_NETDEV_LINK_2500) | \
++                                BIT(TRIGGER_NETDEV_LINK_100))
++
++struct led {
++      unsigned long rules;
++      unsigned long state;
++};
++
++struct en8811h_priv {
++      u32             firmware_version;
++      bool            mcu_needs_restart;
++      struct led      led[EN8811H_LED_COUNT];
++};
++
++enum {
++      AIR_PHY_LED_STATE_FORCE_ON,
++      AIR_PHY_LED_STATE_FORCE_BLINK,
++};
++
++enum {
++      AIR_PHY_LED_DUR_BLINK_32MS,
++      AIR_PHY_LED_DUR_BLINK_64MS,
++      AIR_PHY_LED_DUR_BLINK_128MS,
++      AIR_PHY_LED_DUR_BLINK_256MS,
++      AIR_PHY_LED_DUR_BLINK_512MS,
++      AIR_PHY_LED_DUR_BLINK_1024MS,
++};
++
++enum {
++      AIR_LED_DISABLE,
++      AIR_LED_ENABLE,
++};
++
++enum {
++      AIR_ACTIVE_LOW,
++      AIR_ACTIVE_HIGH,
++};
++
++enum {
++      AIR_LED_MODE_DISABLE,
++      AIR_LED_MODE_USER_DEFINE,
++};
++
++#define AIR_PHY_LED_DUR_UNIT  1024
++#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS)
++
++static const unsigned long en8811h_led_trig = BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
++                                            BIT(TRIGGER_NETDEV_LINK)        |
++                                            BIT(TRIGGER_NETDEV_LINK_10)     |
++                                            BIT(TRIGGER_NETDEV_LINK_100)    |
++                                            BIT(TRIGGER_NETDEV_LINK_1000)   |
++                                            BIT(TRIGGER_NETDEV_LINK_2500)   |
++                                            BIT(TRIGGER_NETDEV_RX)          |
++                                            BIT(TRIGGER_NETDEV_TX);
++
++static int air_phy_read_page(struct phy_device *phydev)
++{
++      return __phy_read(phydev, AIR_EXT_PAGE_ACCESS);
++}
++
++static int air_phy_write_page(struct phy_device *phydev, int page)
++{
++      return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page);
++}
++
++static int __air_buckpbus_reg_write(struct phy_device *phydev,
++                                  u32 pbus_address, u32 pbus_data)
++{
++      int ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
++                        upper_16_bits(pbus_address));
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
++                        lower_16_bits(pbus_address));
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
++                        upper_16_bits(pbus_data));
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
++                        lower_16_bits(pbus_data));
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static int air_buckpbus_reg_write(struct phy_device *phydev,
++                                u32 pbus_address, u32 pbus_data)
++{
++      int saved_page;
++      int ret = 0;
++
++      saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
++
++      if (saved_page >= 0) {
++              ret = __air_buckpbus_reg_write(phydev, pbus_address,
++                                             pbus_data);
++              if (ret < 0)
++                      phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
++                                 pbus_address, ret);
++      }
++
++      return phy_restore_page(phydev, saved_page, ret);
++}
++
++static int __air_buckpbus_reg_read(struct phy_device *phydev,
++                                 u32 pbus_address, u32 *pbus_data)
++{
++      int pbus_data_low, pbus_data_high;
++      int ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
++                        upper_16_bits(pbus_address));
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
++                        lower_16_bits(pbus_address));
++      if (ret < 0)
++              return ret;
++
++      pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
++      if (pbus_data_high < 0)
++              return ret;
++
++      pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
++      if (pbus_data_low < 0)
++              return ret;
++
++      *pbus_data = pbus_data_low | (pbus_data_high << 16);
++      return 0;
++}
++
++static int air_buckpbus_reg_read(struct phy_device *phydev,
++                               u32 pbus_address, u32 *pbus_data)
++{
++      int saved_page;
++      int ret = 0;
++
++      saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
++
++      if (saved_page >= 0) {
++              ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data);
++              if (ret < 0)
++                      phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
++                                 pbus_address, ret);
++      }
++
++      return phy_restore_page(phydev, saved_page, ret);
++}
++
++static int __air_buckpbus_reg_modify(struct phy_device *phydev,
++                                   u32 pbus_address, u32 mask, u32 set)
++{
++      int pbus_data_low, pbus_data_high;
++      u32 pbus_data_old, pbus_data_new;
++      int ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
++                        upper_16_bits(pbus_address));
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
++                        lower_16_bits(pbus_address));
++      if (ret < 0)
++              return ret;
++
++      pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
++      if (pbus_data_high < 0)
++              return ret;
++
++      pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
++      if (pbus_data_low < 0)
++              return ret;
++
++      pbus_data_old = pbus_data_low | (pbus_data_high << 16);
++      pbus_data_new = (pbus_data_old & ~mask) | set;
++      if (pbus_data_new == pbus_data_old)
++              return 0;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
++                        upper_16_bits(pbus_address));
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
++                        lower_16_bits(pbus_address));
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
++                        upper_16_bits(pbus_data_new));
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
++                        lower_16_bits(pbus_data_new));
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static int air_buckpbus_reg_modify(struct phy_device *phydev,
++                                 u32 pbus_address, u32 mask, u32 set)
++{
++      int saved_page;
++      int ret = 0;
++
++      saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
++
++      if (saved_page >= 0) {
++              ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask,
++                                              set);
++              if (ret < 0)
++                      phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
++                                 pbus_address, ret);
++      }
++
++      return phy_restore_page(phydev, saved_page, ret);
++}
++
++static int __air_write_buf(struct phy_device *phydev, u32 address,
++                         const struct firmware *fw)
++{
++      unsigned int offset;
++      int ret;
++      u16 val;
++
++      ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_INCR);
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
++                        upper_16_bits(address));
++      if (ret < 0)
++              return ret;
++
++      ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
++                        lower_16_bits(address));
++      if (ret < 0)
++              return ret;
++
++      for (offset = 0; offset < fw->size; offset += 4) {
++              val = get_unaligned_le16(&fw->data[offset + 2]);
++              ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, val);
++              if (ret < 0)
++                      return ret;
++
++              val = get_unaligned_le16(&fw->data[offset]);
++              ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, val);
++              if (ret < 0)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static int air_write_buf(struct phy_device *phydev, u32 address,
++                       const struct firmware *fw)
++{
++      int saved_page;
++      int ret = 0;
++
++      saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
++
++      if (saved_page >= 0) {
++              ret = __air_write_buf(phydev, address, fw);
++              if (ret < 0)
++                      phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
++                                 address, ret);
++      }
++
++      return phy_restore_page(phydev, saved_page, ret);
++}
++
++static int en8811h_wait_mcu_ready(struct phy_device *phydev)
++{
++      int ret, reg_value;
++
++      /* Because of mdio-lock, may have to wait for multiple loads */
++      ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
++                                      EN8811H_PHY_FW_STATUS, reg_value,
++                                      reg_value == EN8811H_PHY_READY,
++                                      20000, 7500000, true);
++      if (ret) {
++              phydev_err(phydev, "MCU not ready: 0x%x\n", reg_value);
++              return -ENODEV;
++      }
++
++      return 0;
++}
++
++static int en8811h_load_firmware(struct phy_device *phydev)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      struct device *dev = &phydev->mdio.dev;
++      const struct firmware *fw1, *fw2;
++      int ret;
++
++      ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev);
++      if (ret < 0)
++              return ret;
++
++      ret = request_firmware_direct(&fw2, EN8811H_MD32_DSP, dev);
++      if (ret < 0)
++              goto en8811h_load_firmware_rel1;
++
++      ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
++                                   EN8811H_FW_CTRL_1_START);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
++                                    EN8811H_FW_CTRL_2_LOADING,
++                                    EN8811H_FW_CTRL_2_LOADING);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_write_buf(phydev, AIR_FW_ADDR_DM,  fw1);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, fw2);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
++                                    EN8811H_FW_CTRL_2_LOADING, 0);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
++                                   EN8811H_FW_CTRL_1_FINISH);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = en8811h_wait_mcu_ready(phydev);
++
++      air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
++                            &priv->firmware_version);
++      phydev_info(phydev, "MD32 firmware version: %08x\n",
++                  priv->firmware_version);
++
++en8811h_load_firmware_out:
++      release_firmware(fw2);
++
++en8811h_load_firmware_rel1:
++      release_firmware(fw1);
++
++      if (ret < 0)
++              phydev_err(phydev, "Load firmware failed: %d\n", ret);
++
++      return ret;
++}
++
++static int en8811h_restart_mcu(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
++                                   EN8811H_FW_CTRL_1_START);
++      if (ret < 0)
++              return ret;
++
++      ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
++                                   EN8811H_FW_CTRL_1_FINISH);
++      if (ret < 0)
++              return ret;
++
++      return en8811h_wait_mcu_ready(phydev);
++}
++
++static int air_hw_led_on_set(struct phy_device *phydev, u8 index, bool on)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      bool changed;
++
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
++
++      if (on)
++              changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_ON,
++                                          &priv->led[index].state);
++      else
++              changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_ON,
++                                             &priv->led[index].state);
++
++      changed |= (priv->led[index].rules != 0);
++
++      if (changed)
++              return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
++                                    AIR_PHY_LED_ON(index),
++                                    AIR_PHY_LED_ON_MASK,
++                                    on ? AIR_PHY_LED_ON_FORCE_ON : 0);
++
++      return 0;
++}
++
++static int air_hw_led_blink_set(struct phy_device *phydev, u8 index,
++                              bool blinking)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      bool changed;
++
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
++
++      if (blinking)
++              changed = !test_and_set_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
++                                          &priv->led[index].state);
++      else
++              changed = !!test_and_clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
++                                             &priv->led[index].state);
++
++      changed |= (priv->led[index].rules != 0);
++
++      if (changed)
++              return phy_write_mmd(phydev, MDIO_MMD_VEND2,
++                                   AIR_PHY_LED_BLINK(index),
++                                   blinking ?
++                                   AIR_PHY_LED_BLINK_FORCE_BLINK : 0);
++      else
++              return 0;
++}
++
++static int air_led_blink_set(struct phy_device *phydev, u8 index,
++                           unsigned long *delay_on,
++                           unsigned long *delay_off)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      bool blinking = false;
++      int err;
++
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
++
++      if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) {
++              blinking = true;
++              *delay_on = 50;
++              *delay_off = 50;
++      }
++
++      err = air_hw_led_blink_set(phydev, index, blinking);
++      if (err)
++              return err;
++
++      /* led-blink set, so switch led-on off */
++      err = air_hw_led_on_set(phydev, index, false);
++      if (err)
++              return err;
++
++      /* hw-control is off*/
++      if (!!test_bit(AIR_PHY_LED_STATE_FORCE_BLINK, &priv->led[index].state))
++              priv->led[index].rules = 0;
++
++      return 0;
++}
++
++static int air_led_brightness_set(struct phy_device *phydev, u8 index,
++                                enum led_brightness value)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      int err;
++
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
++
++      /* led-on set, so switch led-blink off */
++      err = air_hw_led_blink_set(phydev, index, false);
++      if (err)
++              return err;
++
++      err = air_hw_led_on_set(phydev, index, (value != LED_OFF));
++      if (err)
++              return err;
++
++      /* hw-control is off */
++      if (!!test_bit(AIR_PHY_LED_STATE_FORCE_ON, &priv->led[index].state))
++              priv->led[index].rules = 0;
++
++      return 0;
++}
++
++static int air_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                unsigned long *rules)
++{
++      struct en8811h_priv *priv = phydev->priv;
++
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
++
++      *rules = priv->led[index].rules;
++
++      return 0;
++};
++
++static int air_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                unsigned long rules)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      u16 on = 0, blink = 0;
++      int ret;
++
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
++
++      priv->led[index].rules = rules;
++
++      if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
++              on |= AIR_PHY_LED_ON_FDX;
++
++      if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK)))
++              on |= AIR_PHY_LED_ON_LINK10;
++
++      if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
++              on |= AIR_PHY_LED_ON_LINK100;
++
++      if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
++              on |= AIR_PHY_LED_ON_LINK1000;
++
++      if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
++              on |= AIR_PHY_LED_ON_LINK2500;
++
++      if (rules & BIT(TRIGGER_NETDEV_RX)) {
++              blink |= AIR_PHY_LED_BLINK_10RX   |
++                       AIR_PHY_LED_BLINK_100RX  |
++                       AIR_PHY_LED_BLINK_1000RX |
++                       AIR_PHY_LED_BLINK_2500RX;
++      }
++
++      if (rules & BIT(TRIGGER_NETDEV_TX)) {
++              blink |= AIR_PHY_LED_BLINK_10TX   |
++                       AIR_PHY_LED_BLINK_100TX  |
++                       AIR_PHY_LED_BLINK_1000TX |
++                       AIR_PHY_LED_BLINK_2500TX;
++      }
++
++      if (blink || on) {
++              /* switch hw-control on, so led-on and led-blink are off */
++              clear_bit(AIR_PHY_LED_STATE_FORCE_ON,
++                        &priv->led[index].state);
++              clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
++                        &priv->led[index].state);
++      } else {
++              priv->led[index].rules = 0;
++      }
++
++      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),
++                           AIR_PHY_LED_ON_MASK, on);
++
++      if (ret < 0)
++              return ret;
++
++      return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index),
++                           blink);
++};
++
++static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol)
++{
++      int val = 0;
++      int err;
++
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
++
++      if (state == AIR_LED_ENABLE)
++              val |= AIR_PHY_LED_ON_ENABLE;
++      else
++              val &= ~AIR_PHY_LED_ON_ENABLE;
++
++      if (pol == AIR_ACTIVE_HIGH)
++              val |= AIR_PHY_LED_ON_POLARITY;
++      else
++              val &= ~AIR_PHY_LED_ON_POLARITY;
++
++      err = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),
++                           AIR_PHY_LED_ON_ENABLE |
++                           AIR_PHY_LED_ON_POLARITY, val);
++
++      if (err < 0)
++              return err;
++
++      return 0;
++}
++
++static int air_leds_init(struct phy_device *phydev, int num, int dur, int mode)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      int ret, i;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK,
++                          dur);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON,
++                          dur >> 1);
++      if (ret < 0)
++              return ret;
++
++      switch (mode) {
++      case AIR_LED_MODE_DISABLE:
++              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
++                                   AIR_PHY_LED_BCR_EXT_CTRL |
++                                   AIR_PHY_LED_BCR_MODE_MASK, 0);
++              if (ret < 0)
++                      return ret;
++              break;
++      case AIR_LED_MODE_USER_DEFINE:
++              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
++                                   AIR_PHY_LED_BCR_EXT_CTRL |
++                                   AIR_PHY_LED_BCR_CLK_EN,
++                                   AIR_PHY_LED_BCR_EXT_CTRL |
++                                   AIR_PHY_LED_BCR_CLK_EN);
++              if (ret < 0)
++                      return ret;
++              break;
++      default:
++              phydev_err(phydev, "LED mode %d is not supported\n", mode);
++              return -EINVAL;
++      }
++
++      for (i = 0; i < num; ++i) {
++              ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH);
++              if (ret < 0) {
++                      phydev_err(phydev, "LED%d init failed: %d\n", i, ret);
++                      return ret;
++              }
++              air_led_hw_control_set(phydev, i, priv->led[i].rules);
++      }
++
++      return 0;
++}
++
++static int en8811h_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                     unsigned long rules)
++{
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
++
++      /* All combinations of the supported triggers are allowed */
++      if (rules & ~en8811h_led_trig)
++              return -EOPNOTSUPP;
++
++      return 0;
++};
++
++static int en8811h_probe(struct phy_device *phydev)
++{
++      struct en8811h_priv *priv;
++      int ret;
++
++      priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv),
++                          GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++      phydev->priv = priv;
++
++      ret = en8811h_load_firmware(phydev);
++      if (ret < 0)
++              return ret;
++
++      /* mcu has just restarted after firmware load */
++      priv->mcu_needs_restart = false;
++
++      priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0;
++      priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1;
++      priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2;
++
++      /* MDIO_DEVS1/2 empty, so set mmds_present bits here */
++      phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
++
++      ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,
++                          AIR_LED_MODE_DISABLE);
++      if (ret < 0) {
++              phydev_err(phydev, "Failed to disable leds: %d\n", ret);
++              return ret;
++      }
++
++      /* Configure led gpio pins as output */
++      ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT,
++                                    EN8811H_GPIO_OUTPUT_345,
++                                    EN8811H_GPIO_OUTPUT_345);
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static int en8811h_config_init(struct phy_device *phydev)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      struct device *dev = &phydev->mdio.dev;
++      u32 pbus_value;
++      int ret;
++
++      /* If restart happened in .probe(), no need to restart now */
++      if (priv->mcu_needs_restart) {
++              ret = en8811h_restart_mcu(phydev);
++              if (ret < 0)
++                      return ret;
++      } else {
++              /* Next calls to .config_init() mcu needs to restart */
++              priv->mcu_needs_restart = true;
++      }
++
++      /* Select mode 1, the only mode supported.
++       * Configures the SerDes for 2500Base-X with rate adaptation
++       */
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_1,
++                          AIR_PHY_MCU_CMD_1_MODE1);
++      if (ret < 0)
++              return ret;
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_2,
++                          AIR_PHY_MCU_CMD_2_MODE1);
++      if (ret < 0)
++              return ret;
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3,
++                          AIR_PHY_MCU_CMD_3_MODE1);
++      if (ret < 0)
++              return ret;
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4,
++                          AIR_PHY_MCU_CMD_4_MODE1);
++      if (ret < 0)
++              return ret;
++
++      /* Serdes polarity */
++      pbus_value = 0;
++      if (device_property_read_bool(dev, "airoha,pnswap-rx"))
++              pbus_value |=  EN8811H_POLARITY_RX_REVERSE;
++      else
++              pbus_value &= ~EN8811H_POLARITY_RX_REVERSE;
++      if (device_property_read_bool(dev, "airoha,pnswap-tx"))
++              pbus_value &= ~EN8811H_POLARITY_TX_NORMAL;
++      else
++              pbus_value |=  EN8811H_POLARITY_TX_NORMAL;
++      ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
++                                    EN8811H_POLARITY_RX_REVERSE |
++                                    EN8811H_POLARITY_TX_NORMAL, pbus_value);
++      if (ret < 0)
++              return ret;
++
++      ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,
++                          AIR_LED_MODE_USER_DEFINE);
++      if (ret < 0) {
++              phydev_err(phydev, "Failed to initialize leds: %d\n", ret);
++              return ret;
++      }
++
++      return 0;
++}
++
++static int en8811h_get_features(struct phy_device *phydev)
++{
++      linkmode_set_bit_array(phy_basic_ports_array,
++                             ARRAY_SIZE(phy_basic_ports_array),
++                             phydev->supported);
++
++      return genphy_c45_pma_read_abilities(phydev);
++}
++
++static int en8811h_get_rate_matching(struct phy_device *phydev,
++                                   phy_interface_t iface)
++{
++      return RATE_MATCH_PAUSE;
++}
++
++static int en8811h_config_aneg(struct phy_device *phydev)
++{
++      bool changed = false;
++      int ret;
++      u32 adv;
++
++      if (phydev->autoneg == AUTONEG_DISABLE) {
++              phydev_warn(phydev, "Disabling autoneg is not supported\n");
++              return -EINVAL;
++      }
++
++      adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising);
++
++      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
++                                   MDIO_AN_10GBT_CTRL_ADV2_5G, adv);
++      if (ret < 0)
++              return ret;
++      if (ret > 0)
++              changed = true;
++
++      return __genphy_config_aneg(phydev, changed);
++}
++
++static int en8811h_read_status(struct phy_device *phydev)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      u32 pbus_value;
++      int ret, val;
++
++      ret = genphy_update_link(phydev);
++      if (ret)
++              return ret;
++
++      phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
++      phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
++      phydev->speed = SPEED_UNKNOWN;
++      phydev->duplex = DUPLEX_UNKNOWN;
++      phydev->pause = 0;
++      phydev->asym_pause = 0;
++      phydev->rate_matching = RATE_MATCH_PAUSE;
++
++      ret = genphy_read_master_slave(phydev);
++      if (ret < 0)
++              return ret;
++
++      ret = genphy_read_lpa(phydev);
++      if (ret < 0)
++              return ret;
++
++      /* Get link partner 2.5GBASE-T ability from vendor register */
++      ret = air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, &pbus_value);
++      if (ret < 0)
++              return ret;
++      linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++                       phydev->lp_advertising,
++                       pbus_value & EN8811H_2P5G_LPA_2P5G);
++
++      if (phydev->autoneg_complete)
++              phy_resolve_aneg_pause(phydev);
++
++      if (!phydev->link)
++              return 0;
++
++      /* Get real speed from vendor register */
++      val = phy_read(phydev, AIR_AUX_CTRL_STATUS);
++      if (val < 0)
++              return val;
++      switch (val & AIR_AUX_CTRL_STATUS_SPEED_MASK) {
++      case AIR_AUX_CTRL_STATUS_SPEED_2500:
++              phydev->speed = SPEED_2500;
++              break;
++      case AIR_AUX_CTRL_STATUS_SPEED_1000:
++              phydev->speed = SPEED_1000;
++              break;
++      case AIR_AUX_CTRL_STATUS_SPEED_100:
++              phydev->speed = SPEED_100;
++              break;
++      }
++
++      /* Firmware before version 24011202 has no vendor register 2P5G_LPA.
++       * Assume link partner advertised it if connected at 2500Mbps.
++       */
++      if (priv->firmware_version < 0x24011202) {
++              linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++                               phydev->lp_advertising,
++                               phydev->speed == SPEED_2500);
++      }
++
++      /* Only supports full duplex */
++      phydev->duplex = DUPLEX_FULL;
++
++      return 0;
++}
++
++static int en8811h_clear_intr(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_3,
++                          AIR_PHY_MCU_CMD_3_DOCMD);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, AIR_PHY_MCU_CMD_4,
++                          AIR_PHY_MCU_CMD_4_INTCLR);
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static irqreturn_t en8811h_handle_interrupt(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = en8811h_clear_intr(phydev);
++      if (ret < 0) {
++              phy_error(phydev);
++              return IRQ_NONE;
++      }
++
++      phy_trigger_machine(phydev);
++
++      return IRQ_HANDLED;
++}
++
++static struct phy_driver en8811h_driver[] = {
++{
++      PHY_ID_MATCH_MODEL(EN8811H_PHY_ID),
++      .name                   = "Airoha EN8811H",
++      .probe                  = en8811h_probe,
++      .get_features           = en8811h_get_features,
++      .config_init            = en8811h_config_init,
++      .get_rate_matching      = en8811h_get_rate_matching,
++      .config_aneg            = en8811h_config_aneg,
++      .read_status            = en8811h_read_status,
++      .config_intr            = en8811h_clear_intr,
++      .handle_interrupt       = en8811h_handle_interrupt,
++      .led_hw_is_supported    = en8811h_led_hw_is_supported,
++      .read_page              = air_phy_read_page,
++      .write_page             = air_phy_write_page,
++      .led_blink_set          = air_led_blink_set,
++      .led_brightness_set     = air_led_brightness_set,
++      .led_hw_control_set     = air_led_hw_control_set,
++      .led_hw_control_get     = air_led_hw_control_get,
++} };
++
++module_phy_driver(en8811h_driver);
++
++static struct mdio_device_id __maybe_unused en8811h_tbl[] = {
++      { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) },
++      { }
++};
++
++MODULE_DEVICE_TABLE(mdio, en8811h_tbl);
++MODULE_FIRMWARE(EN8811H_MD32_DM);
++MODULE_FIRMWARE(EN8811H_MD32_DSP);
++
++MODULE_DESCRIPTION("Airoha EN8811H PHY drivers");
++MODULE_AUTHOR("Airoha");
++MODULE_AUTHOR("Eric Woudstra <ericwouds@gmail.com>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.6/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch b/target/linux/generic/backport-6.6/799-v6.10-net-phy-air_en8811h-fix-some-error-codes.patch
new file mode 100644 (file)
index 0000000..1bd0eef
--- /dev/null
@@ -0,0 +1,47 @@
+From 87c33315af380ca12a2e59ac94edad4fe0481b4c Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@linaro.org>
+Date: Fri, 5 Apr 2024 13:08:59 +0300
+Subject: [PATCH] net: phy: air_en8811h: fix some error codes
+
+These error paths accidentally return "ret" which is zero/success
+instead of the correct error code.
+
+Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver")
+Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Link: https://lore.kernel.org/r/7ef2e230-dfb7-4a77-8973-9e5be1a99fc2@moroto.mountain
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/air_en8811h.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/air_en8811h.c
++++ b/drivers/net/phy/air_en8811h.c
+@@ -272,11 +272,11 @@ static int __air_buckpbus_reg_read(struc
+       pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
+       if (pbus_data_high < 0)
+-              return ret;
++              return pbus_data_high;
+       pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
+       if (pbus_data_low < 0)
+-              return ret;
++              return pbus_data_low;
+       *pbus_data = pbus_data_low | (pbus_data_high << 16);
+       return 0;
+@@ -323,11 +323,11 @@ static int __air_buckpbus_reg_modify(str
+       pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
+       if (pbus_data_high < 0)
+-              return ret;
++              return pbus_data_high;
+       pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
+       if (pbus_data_low < 0)
+-              return ret;
++              return pbus_data_low;
+       pbus_data_old = pbus_data_low | (pbus_data_high << 16);
+       pbus_data_new = (pbus_data_old & ~mask) | set;
diff --git a/target/linux/generic/backport-6.6/800-v6.9-0001-dt-bindings-leds-Add-FUNCTION-defines-for-per-band-W.patch b/target/linux/generic/backport-6.6/800-v6.9-0001-dt-bindings-leds-Add-FUNCTION-defines-for-per-band-W.patch
new file mode 100644 (file)
index 0000000..bf36e19
--- /dev/null
@@ -0,0 +1,34 @@
+From ec18a2a83b8b9f7e39c80105ea148c769c46227b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 17 Jan 2024 16:17:36 +0100
+Subject: [PATCH] dt-bindings: leds: Add FUNCTION defines for per-band WLANs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Most wireless routers and access points can operate in multiple bands
+simultaneously. Vendors often equip their devices with per-band LEDs.
+
+Add defines for those very common functions to allow cleaner & clearer
+bindings.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Acked-by: Rob Herring <robh@kernel.org>
+Link: https://lore.kernel.org/r/20240117151736.27440-1-zajec5@gmail.com
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ include/dt-bindings/leds/common.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/include/dt-bindings/leds/common.h
++++ b/include/dt-bindings/leds/common.h
+@@ -101,6 +101,9 @@
+ #define LED_FUNCTION_USB "usb"
+ #define LED_FUNCTION_WAN "wan"
+ #define LED_FUNCTION_WLAN "wlan"
++#define LED_FUNCTION_WLAN_2GHZ "wlan-2ghz"
++#define LED_FUNCTION_WLAN_5GHZ "wlan-5ghz"
++#define LED_FUNCTION_WLAN_6GHZ "wlan-6ghz"
+ #define LED_FUNCTION_WPS "wps"
+ #endif /* __DT_BINDINGS_LEDS_H */
diff --git a/target/linux/generic/backport-6.6/800-v6.9-0002-dt-bindings-leds-Add-LED_FUNCTION_WAN_ONLINE-for-Int.patch b/target/linux/generic/backport-6.6/800-v6.9-0002-dt-bindings-leds-Add-LED_FUNCTION_WAN_ONLINE-for-Int.patch
new file mode 100644 (file)
index 0000000..eabb9bb
--- /dev/null
@@ -0,0 +1,35 @@
+From 64e558500d2d04878b8a6d6578850c475171d6ba Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 23 Feb 2024 12:22:23 +0100
+Subject: [PATCH] dt-bindings: leds: Add LED_FUNCTION_WAN_ONLINE for Internet
+ access
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It's common for routers to have LED indicating link on the WAN port.
+
+Some devices however have an extra LED that's meant to be used if WAN
+connection is actually "online" (there is Internet access available).
+
+It was suggested to add #define for such use case.
+
+Link: https://lore.kernel.org/linux-devicetree/80e92209-5578-44e7-bd4b-603a29053ddf@collabora.com/T/#u
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://lore.kernel.org/r/20240223112223.1368-1-zajec5@gmail.com
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ include/dt-bindings/leds/common.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/dt-bindings/leds/common.h
++++ b/include/dt-bindings/leds/common.h
+@@ -100,6 +100,7 @@
+ #define LED_FUNCTION_TX "tx"
+ #define LED_FUNCTION_USB "usb"
+ #define LED_FUNCTION_WAN "wan"
++#define LED_FUNCTION_WAN_ONLINE "wan-online"
+ #define LED_FUNCTION_WLAN "wlan"
+ #define LED_FUNCTION_WLAN_2GHZ "wlan-2ghz"
+ #define LED_FUNCTION_WLAN_5GHZ "wlan-5ghz"
diff --git a/target/linux/generic/backport-6.6/810-v6.11-hwmon-g672-add-support-for-g761.patch b/target/linux/generic/backport-6.6/810-v6.11-hwmon-g672-add-support-for-g761.patch
new file mode 100644 (file)
index 0000000..96f7a07
--- /dev/null
@@ -0,0 +1,106 @@
+From 6ce402327a6fb714a9f40a0bb59bcbfe383839a5 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 4 Jun 2024 18:43:43 +0200
+Subject: [PATCH] hwmon: g672: add support for g761
+
+Add support for g761 PWM Fan Controller.
+
+The g761 is a copy of the g763 with the only difference of supporting
+and internal clock. The internal clock is used if no clocks property is
+defined in device node and in such case the required bit is enabled and
+clock handling is skipped.
+
+The internal clock oscillator runs at 31KHz.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Link: https://lore.kernel.org/r/20240604164348.542-3-ansuelsmth@gmail.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ drivers/hwmon/g762.c | 33 ++++++++++++++++++++++++++++++---
+ 1 file changed, 30 insertions(+), 3 deletions(-)
+
+--- a/drivers/hwmon/g762.c
++++ b/drivers/hwmon/g762.c
+@@ -69,6 +69,7 @@ enum g762_regs {
+ #define G762_REG_FAN_CMD1_PWM_POLARITY  0x02 /* PWM polarity */
+ #define G762_REG_FAN_CMD1_PULSE_PER_REV 0x01 /* pulse per fan revolution */
++#define G761_REG_FAN_CMD2_FAN_CLOCK     0x20 /* choose internal clock*/
+ #define G762_REG_FAN_CMD2_GEAR_MODE_1   0x08 /* fan gear mode */
+ #define G762_REG_FAN_CMD2_GEAR_MODE_0   0x04
+ #define G762_REG_FAN_CMD2_FAN_STARTV_1  0x02 /* fan startup voltage */
+@@ -115,6 +116,7 @@ enum g762_regs {
+ struct g762_data {
+       struct i2c_client *client;
++      bool internal_clock;
+       struct clk *clk;
+       /* update mutex */
+@@ -566,6 +568,7 @@ static int do_set_fan_startv(struct devi
+ #ifdef CONFIG_OF
+ static const struct of_device_id g762_dt_match[] = {
++      { .compatible = "gmt,g761" },
+       { .compatible = "gmt,g762" },
+       { .compatible = "gmt,g763" },
+       { },
+@@ -597,6 +600,21 @@ static int g762_of_clock_enable(struct i
+       if (!client->dev.of_node)
+               return 0;
++      data = i2c_get_clientdata(client);
++
++      /*
++       * Skip CLK detection and handling if we use internal clock.
++       * This is only valid for g761.
++       */
++      data->internal_clock = of_device_is_compatible(client->dev.of_node,
++                                                     "gmt,g761") &&
++                             !of_property_present(client->dev.of_node,
++                                                  "clocks");
++      if (data->internal_clock) {
++              do_set_clk_freq(&client->dev, 32768);
++              return 0;
++      }
++
+       clk = of_clk_get(client->dev.of_node, 0);
+       if (IS_ERR(clk)) {
+               dev_err(&client->dev, "failed to get clock\n");
+@@ -616,7 +634,6 @@ static int g762_of_clock_enable(struct i
+               goto clk_unprep;
+       }
+-      data = i2c_get_clientdata(client);
+       data->clk = clk;
+       ret = devm_add_action(&client->dev, g762_of_clock_disable, data);
+@@ -1025,16 +1042,26 @@ ATTRIBUTE_GROUPS(g762);
+ static inline int g762_fan_init(struct device *dev)
+ {
+       struct g762_data *data = g762_update_client(dev);
++      int ret;
+       if (IS_ERR(data))
+               return PTR_ERR(data);
++      /* internal_clock can only be set with compatible g761 */
++      if (data->internal_clock)
++              data->fan_cmd2 |= G761_REG_FAN_CMD2_FAN_CLOCK;
++
+       data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_FAIL;
+       data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_OOC;
+       data->valid = false;
+-      return i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
+-                                       data->fan_cmd1);
++      ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
++                                      data->fan_cmd1);
++      if (ret)
++              return ret;
++
++      return i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD2,
++                                       data->fan_cmd2);
+ }
+ static int g762_probe(struct i2c_client *client)
diff --git a/target/linux/generic/backport-6.6/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch b/target/linux/generic/backport-6.6/815-v6.7-2-leds-turris-omnia-Make-set_brightness-more-efficient.patch
new file mode 100644 (file)
index 0000000..d84ad67
--- /dev/null
@@ -0,0 +1,207 @@
+From a64c3c1357275b1fd61bc9734f638cdb5d8a8bbb Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 18 Sep 2023 18:11:02 +0200
+Subject: [PATCH 4/6] leds: turris-omnia: Make set_brightness() more efficient
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Implement caching of the LED color and state values that are sent to MCU
+in order to make the set_brightness() operation more efficient by
+avoiding I2C transactions which are not needed.
+
+On Turris Omnia's MCU, which acts as the RGB LED controller, each LED
+has a RGB color, and a ON/OFF state, which are configurable via I2C
+commands CMD_LED_COLOR and CMD_LED_STATE.
+
+The CMD_LED_COLOR command sends 5 bytes and the CMD_LED_STATE command 2
+bytes over the I2C bus, which operates at 100 kHz. With I2C overhead
+this allows ~1670 color changing commands and ~3200 state changing
+commands per second (or around 1000 color + state changes per second).
+This may seem more than enough, but the issue is that the I2C bus is
+shared with another peripheral, the MCU. The MCU exposes an interrupt
+interface, and it can trigger hundreds of interrupts per second. Each
+time, we need to read the interrupt state register over this I2C bus.
+Whenever we are sending a LED color/state changing command, the
+interrupt reading is waiting.
+
+Currently, every time LED brightness or LED multi intensity is changed,
+we send a CMD_LED_STATE command, and if the computed color (brightness
+adjusted multi_intensity) is non-zero, we also send a CMD_LED_COLOR
+command.
+
+Consider for example the situation when we have a netdev trigger enabled
+for a LED. The netdev trigger does not change the LED color, only the
+brightness (either to 0 or to currently configured brightness), and so
+there is no need to send the CMD_LED_COLOR command. But each change of
+brightness to 0 sends one CMD_LED_STATE command, and each change of
+brightness to max_brightness sends one CMD_LED_STATE command and one
+CMD_LED_COLOR command:
+    set_brightness(0)   ->  CMD_LED_STATE
+    set_brightness(255) ->  CMD_LED_STATE + CMD_LED_COLOR
+                                            (unnecessary)
+
+We can avoid the unnecessary I2C transactions if we cache the values of
+state and color that are sent to the controller. If the color does not
+change from the one previously sent, there is no need to do the
+CMD_LED_COLOR I2C transaction, and if the state does not change, there
+is no need to do the CMD_LED_STATE transaction.
+
+Because we need to make sure that our cached values are consistent with
+the controller state, add explicit setting of the LED color to white at
+probe time (this is the default setting when MCU resets, but does not
+necessarily need to be the case, for example if U-Boot played with the
+LED colors).
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Link: https://lore.kernel.org/r/20230918161104.20860-3-kabel@kernel.org
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/leds-turris-omnia.c | 96 ++++++++++++++++++++++++++------
+ 1 file changed, 78 insertions(+), 18 deletions(-)
+
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -30,6 +30,8 @@
+ struct omnia_led {
+       struct led_classdev_mc mc_cdev;
+       struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS];
++      u8 cached_channels[OMNIA_LED_NUM_CHANNELS];
++      bool on;
+       int reg;
+ };
+@@ -72,36 +74,82 @@ static int omnia_cmd_read_u8(const struc
+               return -EIO;
+ }
++static int omnia_led_send_color_cmd(const struct i2c_client *client,
++                                  struct omnia_led *led)
++{
++      char cmd[5];
++      int ret;
++
++      cmd[0] = CMD_LED_COLOR;
++      cmd[1] = led->reg;
++      cmd[2] = led->subled_info[0].brightness;
++      cmd[3] = led->subled_info[1].brightness;
++      cmd[4] = led->subled_info[2].brightness;
++
++      /* Send the color change command */
++      ret = i2c_master_send(client, cmd, 5);
++      if (ret < 0)
++              return ret;
++
++      /* Cache the RGB channel brightnesses */
++      for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i)
++              led->cached_channels[i] = led->subled_info[i].brightness;
++
++      return 0;
++}
++
++/* Determine if the computed RGB channels are different from the cached ones */
++static bool omnia_led_channels_changed(struct omnia_led *led)
++{
++      for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i)
++              if (led->subled_info[i].brightness != led->cached_channels[i])
++                      return true;
++
++      return false;
++}
++
+ static int omnia_led_brightness_set_blocking(struct led_classdev *cdev,
+                                            enum led_brightness brightness)
+ {
+       struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
+       struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
+       struct omnia_led *led = to_omnia_led(mc_cdev);
+-      u8 buf[5], state;
+-      int ret;
++      int err = 0;
+       mutex_lock(&leds->lock);
+-      led_mc_calc_color_components(&led->mc_cdev, brightness);
++      /*
++       * Only recalculate RGB brightnesses from intensities if brightness is
++       * non-zero. Otherwise we won't be using them and we can save ourselves
++       * some software divisions (Omnia's CPU does not implement the division
++       * instruction).
++       */
++      if (brightness) {
++              led_mc_calc_color_components(mc_cdev, brightness);
++
++              /*
++               * Send color command only if brightness is non-zero and the RGB
++               * channel brightnesses changed.
++               */
++              if (omnia_led_channels_changed(led))
++                      err = omnia_led_send_color_cmd(leds->client, led);
++      }
+-      buf[0] = CMD_LED_COLOR;
+-      buf[1] = led->reg;
+-      buf[2] = mc_cdev->subled_info[0].brightness;
+-      buf[3] = mc_cdev->subled_info[1].brightness;
+-      buf[4] = mc_cdev->subled_info[2].brightness;
+-
+-      state = CMD_LED_STATE_LED(led->reg);
+-      if (buf[2] || buf[3] || buf[4])
+-              state |= CMD_LED_STATE_ON;
+-
+-      ret = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state);
+-      if (ret >= 0 && (state & CMD_LED_STATE_ON))
+-              ret = i2c_master_send(leds->client, buf, 5);
++      /* Send on/off state change only if (bool)brightness changed */
++      if (!err && !brightness != !led->on) {
++              u8 state = CMD_LED_STATE_LED(led->reg);
++
++              if (brightness)
++                      state |= CMD_LED_STATE_ON;
++
++              err = omnia_cmd_write_u8(leds->client, CMD_LED_STATE, state);
++              if (!err)
++                      led->on = !!brightness;
++      }
+       mutex_unlock(&leds->lock);
+-      return ret;
++      return err;
+ }
+ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led,
+@@ -129,11 +177,15 @@ static int omnia_led_register(struct i2c
+       }
+       led->subled_info[0].color_index = LED_COLOR_ID_RED;
+-      led->subled_info[0].channel = 0;
+       led->subled_info[1].color_index = LED_COLOR_ID_GREEN;
+-      led->subled_info[1].channel = 1;
+       led->subled_info[2].color_index = LED_COLOR_ID_BLUE;
+-      led->subled_info[2].channel = 2;
++
++      /* Initial color is white */
++      for (int i = 0; i < OMNIA_LED_NUM_CHANNELS; ++i) {
++              led->subled_info[i].intensity = 255;
++              led->subled_info[i].brightness = 255;
++              led->subled_info[i].channel = i;
++      }
+       led->mc_cdev.subled_info = led->subled_info;
+       led->mc_cdev.num_colors = OMNIA_LED_NUM_CHANNELS;
+@@ -162,6 +214,14 @@ static int omnia_led_register(struct i2c
+               return ret;
+       }
++      /* Set initial color and cache it */
++      ret = omnia_led_send_color_cmd(client, led);
++      if (ret < 0) {
++              dev_err(dev, "Cannot set LED %pOF initial color: %i\n", np,
++                      ret);
++              return ret;
++      }
++
+       ret = devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev,
+                                                       &init_data);
+       if (ret < 0) {
diff --git a/target/linux/generic/backport-6.6/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch b/target/linux/generic/backport-6.6/815-v6.7-3-leds-turris-omnia-Support-HW-controlled-mode-via-pri.patch
new file mode 100644 (file)
index 0000000..bed20f9
--- /dev/null
@@ -0,0 +1,202 @@
+From e965e0f6de60874fc0a0caed9a9e0122999e0c7b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 18 Sep 2023 18:11:03 +0200
+Subject: [PATCH 5/6] 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". Recall that private LED
+triggers will only be listed in the sysfs trigger file for LEDs that
+support them (currently there is no user of this mechanism).
+
+When in MCU controlled mode, the user can still set LED color, but the
+blinking is done by MCU, which does different things for different 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 the
+  corresponding port of the LAN switch
+- PCIe LEDs are blinked according to the logical OR of the MiniPCIe port
+  LED pins
+
+In the future I want to make the netdev trigger to transparently offload
+the blinking to the HW if user sets compatible settings for the netdev
+trigger (for LEDs associated with network devices).
+There was some work on this already, and hopefully we will be able to
+complete it sometime, but for now there are still multiple blockers for
+this, and even if there weren't, we still would not be able to configure
+HW controlled mode for the LEDs associated with MiniPCIe ports.
+
+In the meantime let's support HW controlled mode via the private LED
+trigger mechanism. If, in the future, we manage to complete the netdev
+trigger offloading, we can still keep this private trigger for backwards
+compatibility, 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>
+Link: https://lore.kernel.org/r/20230918161104.20860-4-kabel@kernel.org
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/Kconfig             |  1 +
+ drivers/leds/leds-turris-omnia.c | 98 +++++++++++++++++++++++++++++---
+ 2 files changed, 91 insertions(+), 8 deletions(-)
+
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -188,6 +188,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
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -31,7 +31,7 @@ struct omnia_led {
+       struct led_classdev_mc mc_cdev;
+       struct mc_subled subled_info[OMNIA_LED_NUM_CHANNELS];
+       u8 cached_channels[OMNIA_LED_NUM_CHANNELS];
+-      bool on;
++      bool on, hwtrig;
+       int reg;
+ };
+@@ -120,12 +120,14 @@ static int omnia_led_brightness_set_bloc
+       /*
+        * Only recalculate RGB brightnesses from intensities if brightness is
+-       * non-zero. Otherwise we won't be using them and we can save ourselves
+-       * some software divisions (Omnia's CPU does not implement the division
+-       * instruction).
++       * non-zero (if it is zero and the LED is in HW blinking mode, we use
++       * max_brightness as brightness). Otherwise we won't be using them and
++       * we can save ourselves some software divisions (Omnia's CPU does not
++       * implement the division instruction).
+        */
+-      if (brightness) {
+-              led_mc_calc_color_components(mc_cdev, brightness);
++      if (brightness || led->hwtrig) {
++              led_mc_calc_color_components(mc_cdev, brightness ?:
++                                                    cdev->max_brightness);
+               /*
+                * Send color command only if brightness is non-zero and the RGB
+@@ -135,8 +137,11 @@ static int omnia_led_brightness_set_bloc
+                       err = omnia_led_send_color_cmd(leds->client, led);
+       }
+-      /* Send on/off state change only if (bool)brightness changed */
+-      if (!err && !brightness != !led->on) {
++      /*
++       * Send on/off state change only if (bool)brightness changed and the LED
++       * is not being blinked by HW.
++       */
++      if (!err && !led->hwtrig && !brightness != !led->on) {
+               u8 state = CMD_LED_STATE_LED(led->reg);
+               if (brightness)
+@@ -152,6 +157,71 @@ static int omnia_led_brightness_set_bloc
+       return err;
+ }
++static struct led_hw_trigger_type omnia_hw_trigger_type;
++
++static int omnia_hwtrig_activate(struct led_classdev *cdev)
++{
++      struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
++      struct omnia_leds *leds = dev_get_drvdata(cdev->dev->parent);
++      struct omnia_led *led = to_omnia_led(mc_cdev);
++      int err = 0;
++
++      mutex_lock(&leds->lock);
++
++      if (!led->on) {
++              /*
++               * If the LED is off (brightness was set to 0), the last
++               * configured color was not necessarily sent to the MCU.
++               * Recompute with max_brightness and send if needed.
++               */
++              led_mc_calc_color_components(mc_cdev, cdev->max_brightness);
++
++              if (omnia_led_channels_changed(led))
++                      err = omnia_led_send_color_cmd(leds->client, led);
++      }
++
++      if (!err) {
++              /* Put the LED into MCU controlled mode */
++              err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE,
++                                       CMD_LED_MODE_LED(led->reg));
++              if (!err)
++                      led->hwtrig = true;
++      }
++
++      mutex_unlock(&leds->lock);
++
++      return err;
++}
++
++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 err;
++
++      mutex_lock(&leds->lock);
++
++      led->hwtrig = false;
++
++      /* Put the LED into software mode */
++      err = omnia_cmd_write_u8(leds->client, CMD_LED_MODE,
++                               CMD_LED_MODE_LED(led->reg) |
++                               CMD_LED_MODE_USER);
++
++      mutex_unlock(&leds->lock);
++
++      if (err < 0)
++              dev_err(cdev->dev, "Cannot put LED to software mode: %i\n",
++                      err);
++}
++
++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_register(struct i2c_client *client, struct omnia_led *led,
+                             struct device_node *np)
+ {
+@@ -195,6 +265,12 @@ static int omnia_led_register(struct i2c
+       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;
++      /*
++       * Use the omnia-mcu trigger as the default trigger. It may be rewritten
++       * by LED class from the linux,default-trigger property.
++       */
++      cdev->default_trigger = omnia_hw_trigger.name;
+       /* put the LED into software mode */
+       ret = omnia_cmd_write_u8(client, CMD_LED_MODE,
+@@ -308,6 +384,12 @@ static int omnia_leds_probe(struct i2c_c
+       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);
diff --git a/target/linux/generic/backport-6.6/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch b/target/linux/generic/backport-6.6/815-v6.7-4-leds-turris-omnia-Add-support-for-enabling-disabling.patch
new file mode 100644 (file)
index 0000000..813e6e5
--- /dev/null
@@ -0,0 +1,244 @@
+From 0efb3f9609d3de5a7d8c31e3835d7eb3e6adce79 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 18 Sep 2023 18:11:04 +0200
+Subject: [PATCH 6/6] leds: turris-omnia: Add support for enabling/disabling HW
+ gamma correction
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If the MCU on Turris Omnia is running newer firmware versions, the LED
+controller supports RGB gamma correction (and enables it by default for
+newer boards).
+
+Determine whether the gamma correction setting feature is supported and
+add the ability to set it via sysfs attribute file.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Link: https://lore.kernel.org/r/20230918161104.20860-5-kabel@kernel.org
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ .../sysfs-class-led-driver-turris-omnia       |  14 ++
+ drivers/leds/leds-turris-omnia.c              | 137 +++++++++++++++---
+ 2 files changed, 134 insertions(+), 17 deletions(-)
+
+--- a/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia
++++ b/Documentation/ABI/testing/sysfs-class-led-driver-turris-omnia
+@@ -12,3 +12,17 @@ Description:        (RW) On the front panel of
+               able to change this setting from software.
+               Format: %i
++
++What:         /sys/class/leds/<led>/device/gamma_correction
++Date:         August 2023
++KernelVersion:        6.6
++Contact:      Marek Behún <kabel@kernel.org>
++Description:  (RW) Newer versions of the microcontroller firmware of the
++              Turris Omnia router support gamma correction for the RGB LEDs.
++              This feature can be enabled/disabled by writing to this file.
++
++              If the feature is not supported because the MCU firmware is too
++              old, the file always reads as 0, and writing to the file results
++              in the EOPNOTSUPP error.
++
++              Format: %i
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -15,17 +15,30 @@
+ #define OMNIA_BOARD_LEDS      12
+ #define OMNIA_LED_NUM_CHANNELS        3
+-#define CMD_LED_MODE          3
+-#define CMD_LED_MODE_LED(l)   ((l) & 0x0f)
+-#define CMD_LED_MODE_USER     0x10
+-
+-#define CMD_LED_STATE         4
+-#define CMD_LED_STATE_LED(l)  ((l) & 0x0f)
+-#define CMD_LED_STATE_ON      0x10
+-
+-#define CMD_LED_COLOR         5
+-#define CMD_LED_SET_BRIGHTNESS        7
+-#define CMD_LED_GET_BRIGHTNESS        8
++/* MCU controller commands at I2C address 0x2a */
++#define OMNIA_MCU_I2C_ADDR            0x2a
++
++#define CMD_GET_STATUS_WORD           0x01
++#define STS_FEATURES_SUPPORTED                BIT(2)
++
++#define CMD_GET_FEATURES              0x10
++#define FEAT_LED_GAMMA_CORRECTION     BIT(5)
++
++/* LED controller commands at I2C address 0x2b */
++#define CMD_LED_MODE                  0x03
++#define CMD_LED_MODE_LED(l)           ((l) & 0x0f)
++#define CMD_LED_MODE_USER             0x10
++
++#define CMD_LED_STATE                 0x04
++#define CMD_LED_STATE_LED(l)          ((l) & 0x0f)
++#define CMD_LED_STATE_ON              0x10
++
++#define CMD_LED_COLOR                 0x05
++#define CMD_LED_SET_BRIGHTNESS                0x07
++#define CMD_LED_GET_BRIGHTNESS                0x08
++
++#define CMD_SET_GAMMA_CORRECTION      0x30
++#define CMD_GET_GAMMA_CORRECTION      0x31
+ struct omnia_led {
+       struct led_classdev_mc mc_cdev;
+@@ -40,6 +53,7 @@ struct omnia_led {
+ struct omnia_leds {
+       struct i2c_client *client;
+       struct mutex lock;
++      bool has_gamma_correction;
+       struct omnia_led leds[];
+ };
+@@ -50,30 +64,42 @@ static int omnia_cmd_write_u8(const stru
+       return i2c_master_send(client, buf, sizeof(buf));
+ }
+-static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
++static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd,
++                            void *reply, size_t len)
+ {
+       struct i2c_msg msgs[2];
+-      u8 reply;
+       int ret;
+-      msgs[0].addr = client->addr;
++      msgs[0].addr = addr;
+       msgs[0].flags = 0;
+       msgs[0].len = 1;
+       msgs[0].buf = &cmd;
+-      msgs[1].addr = client->addr;
++      msgs[1].addr = addr;
+       msgs[1].flags = I2C_M_RD;
+-      msgs[1].len = 1;
+-      msgs[1].buf = &reply;
++      msgs[1].len = len;
++      msgs[1].buf = reply;
+-      ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++      ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
+       if (likely(ret == ARRAY_SIZE(msgs)))
+-              return reply;
++              return len;
+       else if (ret < 0)
+               return ret;
+       else
+               return -EIO;
+ }
++static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
++{
++      u8 reply;
++      int ret;
++
++      ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
++      if (ret < 0)
++              return ret;
++
++      return reply;
++}
++
+ static int omnia_led_send_color_cmd(const struct i2c_client *client,
+                                   struct omnia_led *led)
+ {
+@@ -352,12 +378,74 @@ static ssize_t brightness_store(struct d
+ }
+ static DEVICE_ATTR_RW(brightness);
++static ssize_t gamma_correction_show(struct device *dev,
++                                   struct device_attribute *a, char *buf)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct omnia_leds *leds = i2c_get_clientdata(client);
++      int ret;
++
++      if (leds->has_gamma_correction) {
++              ret = omnia_cmd_read_u8(client, CMD_GET_GAMMA_CORRECTION);
++              if (ret < 0)
++                      return ret;
++      } else {
++              ret = 0;
++      }
++
++      return sysfs_emit(buf, "%d\n", !!ret);
++}
++
++static ssize_t gamma_correction_store(struct device *dev,
++                                    struct device_attribute *a,
++                                    const char *buf, size_t count)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct omnia_leds *leds = i2c_get_clientdata(client);
++      bool val;
++      int ret;
++
++      if (!leds->has_gamma_correction)
++              return -EOPNOTSUPP;
++
++      if (kstrtobool(buf, &val) < 0)
++              return -EINVAL;
++
++      ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
++
++      return ret < 0 ? ret : count;
++}
++static DEVICE_ATTR_RW(gamma_correction);
++
+ static struct attribute *omnia_led_controller_attrs[] = {
+       &dev_attr_brightness.attr,
++      &dev_attr_gamma_correction.attr,
+       NULL,
+ };
+ ATTRIBUTE_GROUPS(omnia_led_controller);
++static int omnia_mcu_get_features(const struct i2c_client *client)
++{
++      u16 reply;
++      int err;
++
++      err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
++                               CMD_GET_STATUS_WORD, &reply, sizeof(reply));
++      if (err < 0)
++              return err;
++
++      /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */
++      if (!(le16_to_cpu(reply) & STS_FEATURES_SUPPORTED))
++              return 0;
++
++      err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
++                               CMD_GET_FEATURES, &reply, sizeof(reply));
++      if (err < 0)
++              return err;
++
++      return le16_to_cpu(reply);
++}
++
+ static int omnia_leds_probe(struct i2c_client *client)
+ {
+       struct device *dev = &client->dev;
+@@ -382,6 +470,21 @@ static int omnia_leds_probe(struct i2c_c
+       leds->client = client;
+       i2c_set_clientdata(client, leds);
++      ret = omnia_mcu_get_features(client);
++      if (ret < 0) {
++              dev_err(dev, "Cannot determine MCU supported features: %d\n",
++                      ret);
++              return ret;
++      }
++
++      leds->has_gamma_correction = ret & FEAT_LED_GAMMA_CORRECTION;
++      if (!leds->has_gamma_correction) {
++              dev_info(dev,
++                       "Your board's MCU firmware does not support the LED gamma correction feature.\n");
++              dev_info(dev,
++                       "Consider upgrading MCU firmware with the omnia-mcutool utility.\n");
++      }
++
+       mutex_init(&leds->lock);
+       ret = devm_led_trigger_register(dev, &omnia_hw_trigger);
diff --git a/target/linux/generic/backport-6.6/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch b/target/linux/generic/backport-6.6/815-v6.7-5-leds-turris-omnia-Fix-brightness-setting-and-trigger.patch
new file mode 100644 (file)
index 0000000..b0cebdc
--- /dev/null
@@ -0,0 +1,167 @@
+From ffec49d391c5f0195360912b216aa24dbc9b53c8 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Mon, 16 Oct 2023 16:15:38 +0200
+Subject: [PATCH] leds: turris-omnia: Fix brightness setting and trigger
+ activating
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+I have improperly refactored commits
+  4d5ed2621c24 ("leds: turris-omnia: Make set_brightness() more efficient")
+and
+  aaf38273cf76 ("leds: turris-omnia: Support HW controlled mode via private trigger")
+after Lee requested a change in API semantics of the new functions I
+introduced in commit
+  28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls").
+
+Before the change, the function omnia_cmd_write_u8() returned 0 on
+success, and afterwards it returned a positive value (number of bytes
+written). The latter version was applied, but the following commits did
+not properly account for this change.
+
+This results in non-functional LED's .brightness_set_blocking() and
+trigger's .activate() methods.
+
+The main reasoning behind the semantics change was that read/write
+methods should return the number of read/written bytes on success.
+It was pointed to me [1] that this is not always true (for example the
+regmap API does not do so), and since the driver never uses this number
+of read/written bytes information, I decided to fix this issue by
+changing the functions to the original semantics (return 0 on success).
+
+[1] https://lore.kernel.org/linux-gpio/ZQnn+Gi0xVlsGCYA@smile.fi.intel.com/
+
+Fixes: 28350bc0ac77 ("leds: turris-omnia: Do not use SMBUS calls")
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/leds/leds-turris-omnia.c | 37 +++++++++++++++++---------------
+ 1 file changed, 20 insertions(+), 17 deletions(-)
+
+--- a/drivers/leds/leds-turris-omnia.c
++++ b/drivers/leds/leds-turris-omnia.c
+@@ -60,8 +60,11 @@ struct omnia_leds {
+ static int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd, u8 val)
+ {
+       u8 buf[2] = { cmd, val };
++      int ret;
++
++      ret = i2c_master_send(client, buf, sizeof(buf));
+-      return i2c_master_send(client, buf, sizeof(buf));
++      return ret < 0 ? ret : 0;
+ }
+ static int omnia_cmd_read_raw(struct i2c_adapter *adapter, u8 addr, u8 cmd,
+@@ -81,7 +84,7 @@ static int omnia_cmd_read_raw(struct i2c
+       ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
+       if (likely(ret == ARRAY_SIZE(msgs)))
+-              return len;
++              return 0;
+       else if (ret < 0)
+               return ret;
+       else
+@@ -91,11 +94,11 @@ static int omnia_cmd_read_raw(struct i2c
+ static int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd)
+ {
+       u8 reply;
+-      int ret;
++      int err;
+-      ret = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
+-      if (ret < 0)
+-              return ret;
++      err = omnia_cmd_read_raw(client->adapter, client->addr, cmd, &reply, 1);
++      if (err)
++              return err;
+       return reply;
+ }
+@@ -236,7 +239,7 @@ static void omnia_hwtrig_deactivate(stru
+       mutex_unlock(&leds->lock);
+-      if (err < 0)
++      if (err)
+               dev_err(cdev->dev, "Cannot put LED to software mode: %i\n",
+                       err);
+ }
+@@ -302,7 +305,7 @@ static int omnia_led_register(struct i2c
+       ret = omnia_cmd_write_u8(client, CMD_LED_MODE,
+                                CMD_LED_MODE_LED(led->reg) |
+                                CMD_LED_MODE_USER);
+-      if (ret < 0) {
++      if (ret) {
+               dev_err(dev, "Cannot set LED %pOF to software mode: %i\n", np,
+                       ret);
+               return ret;
+@@ -311,7 +314,7 @@ static int omnia_led_register(struct i2c
+       /* disable the LED */
+       ret = omnia_cmd_write_u8(client, CMD_LED_STATE,
+                                CMD_LED_STATE_LED(led->reg));
+-      if (ret < 0) {
++      if (ret) {
+               dev_err(dev, "Cannot set LED %pOF brightness: %i\n", np, ret);
+               return ret;
+       }
+@@ -364,7 +367,7 @@ static ssize_t brightness_store(struct d
+ {
+       struct i2c_client *client = to_i2c_client(dev);
+       unsigned long brightness;
+-      int ret;
++      int err;
+       if (kstrtoul(buf, 10, &brightness))
+               return -EINVAL;
+@@ -372,9 +375,9 @@ static ssize_t brightness_store(struct d
+       if (brightness > 100)
+               return -EINVAL;
+-      ret = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness);
++      err = omnia_cmd_write_u8(client, CMD_LED_SET_BRIGHTNESS, brightness);
+-      return ret < 0 ? ret : count;
++      return err ?: count;
+ }
+ static DEVICE_ATTR_RW(brightness);
+@@ -403,7 +406,7 @@ static ssize_t gamma_correction_store(st
+       struct i2c_client *client = to_i2c_client(dev);
+       struct omnia_leds *leds = i2c_get_clientdata(client);
+       bool val;
+-      int ret;
++      int err;
+       if (!leds->has_gamma_correction)
+               return -EOPNOTSUPP;
+@@ -411,9 +414,9 @@ static ssize_t gamma_correction_store(st
+       if (kstrtobool(buf, &val) < 0)
+               return -EINVAL;
+-      ret = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
++      err = omnia_cmd_write_u8(client, CMD_SET_GAMMA_CORRECTION, val);
+-      return ret < 0 ? ret : count;
++      return err ?: count;
+ }
+ static DEVICE_ATTR_RW(gamma_correction);
+@@ -431,7 +434,7 @@ static int omnia_mcu_get_features(const
+       err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
+                                CMD_GET_STATUS_WORD, &reply, sizeof(reply));
+-      if (err < 0)
++      if (err)
+               return err;
+       /* Check whether MCU firmware supports the CMD_GET_FEAUTRES command */
+@@ -440,7 +443,7 @@ static int omnia_mcu_get_features(const
+       err = omnia_cmd_read_raw(client->adapter, OMNIA_MCU_I2C_ADDR,
+                                CMD_GET_FEATURES, &reply, sizeof(reply));
+-      if (err < 0)
++      if (err)
+               return err;
+       return le16_to_cpu(reply);
diff --git a/target/linux/generic/backport-6.6/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch b/target/linux/generic/backport-6.6/816-v6.7-0001-nvmem-qfprom-Mark-core-clk-as-optional.patch
new file mode 100644 (file)
index 0000000..c83d4fc
--- /dev/null
@@ -0,0 +1,37 @@
+From 16724d6ea40a2c9315f5a0d81005dfa4d7a6da24 Mon Sep 17 00:00:00 2001
+From: Luca Weiss <luca.weiss@fairphone.com>
+Date: Fri, 20 Oct 2023 11:55:40 +0100
+Subject: [PATCH] nvmem: qfprom: Mark core clk as optional
+
+On some platforms like sc7280 on non-ChromeOS devices the core clock
+cannot be touched by Linux so we cannot provide it. Mark it as optional
+as accessing qfprom for reading works without it but we still prohibit
+writing if we cannot provide the clock.
+
+Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
+Reviewed-by: Douglas Anderson <dianders@chromium.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231020105545.216052-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/qfprom.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/qfprom.c
++++ b/drivers/nvmem/qfprom.c
+@@ -424,12 +424,12 @@ static int qfprom_probe(struct platform_
+               if (IS_ERR(priv->vcc))
+                       return PTR_ERR(priv->vcc);
+-              priv->secclk = devm_clk_get(dev, "core");
++              priv->secclk = devm_clk_get_optional(dev, "core");
+               if (IS_ERR(priv->secclk))
+                       return dev_err_probe(dev, PTR_ERR(priv->secclk), "Error getting clock\n");
+-              /* Only enable writing if we have SoC data. */
+-              if (priv->soc_data)
++              /* Only enable writing if we have SoC data and a valid clock */
++              if (priv->soc_data && priv->secclk)
+                       econfig.reg_write = qfprom_reg_write;
+       }
diff --git a/target/linux/generic/backport-6.6/816-v6.7-0003-nvmem-Use-device_get_match_data.patch b/target/linux/generic/backport-6.6/816-v6.7-0003-nvmem-Use-device_get_match_data.patch
new file mode 100644 (file)
index 0000000..84c0293
--- /dev/null
@@ -0,0 +1,77 @@
+From 0720219f4d34a88a9badb4de70cfad7585687d48 Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Fri, 20 Oct 2023 11:55:45 +0100
+Subject: [PATCH] nvmem: Use device_get_match_data()
+
+Use preferred device_get_match_data() instead of of_match_device() to
+get the driver match data. With this, adjust the includes to explicitly
+include the correct headers.
+
+Signed-off-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231020105545.216052-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mxs-ocotp.c   | 10 ++++------
+ drivers/nvmem/stm32-romem.c |  7 ++++---
+ 2 files changed, 8 insertions(+), 9 deletions(-)
+
+--- a/drivers/nvmem/mxs-ocotp.c
++++ b/drivers/nvmem/mxs-ocotp.c
+@@ -13,8 +13,9 @@
+ #include <linux/io.h>
+ #include <linux/module.h>
+ #include <linux/nvmem-provider.h>
+-#include <linux/of_device.h>
++#include <linux/of.h>
+ #include <linux/platform_device.h>
++#include <linux/property.h>
+ #include <linux/slab.h>
+ #include <linux/stmp_device.h>
+@@ -140,11 +141,10 @@ static int mxs_ocotp_probe(struct platfo
+       struct device *dev = &pdev->dev;
+       const struct mxs_data *data;
+       struct mxs_ocotp *otp;
+-      const struct of_device_id *match;
+       int ret;
+-      match = of_match_device(dev->driver->of_match_table, dev);
+-      if (!match || !match->data)
++      data = device_get_match_data(dev);
++      if (!data)
+               return -EINVAL;
+       otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
+@@ -169,8 +169,6 @@ static int mxs_ocotp_probe(struct platfo
+       if (ret)
+               return ret;
+-      data = match->data;
+-
+       ocotp_config.size = data->size;
+       ocotp_config.priv = otp;
+       ocotp_config.dev = dev;
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -10,7 +10,9 @@
+ #include <linux/io.h>
+ #include <linux/module.h>
+ #include <linux/nvmem-provider.h>
+-#include <linux/of_device.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
+ #include <linux/tee_drv.h>
+ #include "stm32-bsec-optee-ta.h"
+@@ -211,8 +213,7 @@ static int stm32_romem_probe(struct plat
+       priv->lower = 0;
+-      cfg = (const struct stm32_romem_cfg *)
+-              of_match_device(dev->driver->of_match_table, dev)->data;
++      cfg = device_get_match_data(dev);
+       if (!cfg) {
+               priv->cfg.read_only = true;
+               priv->cfg.size = resource_size(res);
diff --git a/target/linux/generic/backport-6.6/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch b/target/linux/generic/backport-6.6/816-v6.7-0004-Revert-nvmem-add-new-config-option.patch
new file mode 100644 (file)
index 0000000..36d1524
--- /dev/null
@@ -0,0 +1,77 @@
+From f4cf4e5db331a5ce69e3f0b21d322cac0f4e4b5d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 23 Oct 2023 12:27:59 +0200
+Subject: [PATCH] Revert "nvmem: add new config option"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit 517f14d9cf3533d5ab4fded195ab6f80a92e378f.
+
+Config option "no_of_node" is no longer needed since adding a more
+explicit and targeted option "add_legacy_fixed_of_cells".
+
+That "no_of_node" config option was needed *earlier* to help mtd's case.
+
+DT nodes of MTD partitions (that are also NVMEM devices) may contain
+subnodes. Those SHOULD NOT be treated as NVMEM fixed cells.
+
+To prevent NVMEM core code from parsing subnodes a "no_of_node" option
+was added (and set to true in mtd) to make for_each_child_of_node() in
+NVMEM a no-op. That was a bit hacky because it was messing with
+"of_node" pointer to achieve some side-effect.
+
+With the introduction of "add_legacy_fixed_of_cells" config option
+things got more explicit. MTD subsystem simply tells NVMEM when to look
+for fixed cells and there is no need to hack "of_node" pointer anymore.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231023102759.31529-1-zajec5@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/mtdcore.c          | 1 -
+ drivers/nvmem/core.c           | 2 +-
+ include/linux/nvmem-provider.h | 2 --
+ 3 files changed, 1 insertion(+), 4 deletions(-)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -560,7 +560,6 @@ static int mtd_nvmem_add(struct mtd_info
+       config.read_only = true;
+       config.root_only = true;
+       config.ignore_wp = true;
+-      config.no_of_node = !of_device_is_compatible(node, "nvmem-cells");
+       config.priv = mtd;
+       mtd->nvmem = nvmem_register(&config);
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -940,7 +940,7 @@ struct nvmem_device *nvmem_register(cons
+       nvmem->nkeepout = config->nkeepout;
+       if (config->of_node)
+               nvmem->dev.of_node = config->of_node;
+-      else if (!config->no_of_node)
++      else
+               nvmem->dev.of_node = config->dev->of_node;
+       switch (config->id) {
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -89,7 +89,6 @@ struct nvmem_cell_info {
+  * @read_only:        Device is read-only.
+  * @root_only:        Device is accessibly to root only.
+  * @of_node:  If given, this will be used instead of the parent's of_node.
+- * @no_of_node:       Device should not use the parent's of_node even if it's !NULL.
+  * @reg_read: Callback to read data.
+  * @reg_write:        Callback to write data.
+  * @size:     Device size.
+@@ -122,7 +121,6 @@ struct nvmem_config {
+       bool                    ignore_wp;
+       struct nvmem_layout     *layout;
+       struct device_node      *of_node;
+-      bool                    no_of_node;
+       nvmem_reg_read_t        reg_read;
+       nvmem_reg_write_t       reg_write;
+       int     size;
diff --git a/target/linux/generic/backport-6.6/817-v6.9-of-property-Make-no-port-node-found-output-a-debug.patch b/target/linux/generic/backport-6.6/817-v6.9-of-property-Make-no-port-node-found-output-a-debug.patch
new file mode 100644 (file)
index 0000000..bc4e5e7
--- /dev/null
@@ -0,0 +1,33 @@
+From e20cd62b1f1708a4dec7ff4beb9e748a0bdb5716 Mon Sep 17 00:00:00 2001
+From: Alexander Stein <alexander.stein@ew.tq-group.com>
+Date: Wed, 17 Jan 2024 09:32:06 +0100
+Subject: [PATCH] of: property: Make 'no port node found' output a debug
+ message
+
+There are cases where an unavailable port is not an error, making this
+error message a false-positive. Since commit d56de8c9a17d8 ("usb: typec:
+tcpm: try to get role switch from tcpc fwnode") the role switch is tried
+on the port dev first and tcpc fwnode afterwards. If using the latter
+bindings getting from port dev fails every time. The kernel log is flooded
+with the messages like:
+ OF: graph: no port node found in /soc@0/bus@42000000/i2c@42530000/usb-typec@50
+Silence this message by making it a debug message.
+
+Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
+Link: https://lore.kernel.org/r/20240117083206.2901534-1-alexander.stein@ew.tq-group.com
+Signed-off-by: Rob Herring <robh@kernel.org>
+---
+ drivers/of/property.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/of/property.c
++++ b/drivers/of/property.c
+@@ -664,7 +664,7 @@ struct device_node *of_graph_get_next_en
+               of_node_put(node);
+               if (!port) {
+-                      pr_err("graph: no port node found in %pOF\n", parent);
++                      pr_debug("graph: no port node found in %pOF\n", parent);
+                       return NULL;
+               }
+       } else {
diff --git a/target/linux/generic/backport-6.6/818-v6.8-of-device-Export-of_device_make_bus_id.patch b/target/linux/generic/backport-6.6/818-v6.8-of-device-Export-of_device_make_bus_id.patch
new file mode 100644 (file)
index 0000000..95e1a7b
--- /dev/null
@@ -0,0 +1,140 @@
+From 7f38b70042fcaa49219045bd1a9a2836e27a58ac Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:27 +0000
+Subject: [PATCH] of: device: Export of_device_make_bus_id()
+
+This helper is really handy to create unique device names based on their
+device tree path, we may need it outside of the OF core (in the NVMEM
+subsystem) so let's export it. As this helper has nothing patform
+specific, let's move it to of/device.c instead of of/platform.c so we
+can add its prototype to of_device.h.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Acked-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/of/device.c       | 41 +++++++++++++++++++++++++++++++++++++++
+ drivers/of/platform.c     | 40 --------------------------------------
+ include/linux/of_device.h |  6 ++++++
+ 3 files changed, 47 insertions(+), 40 deletions(-)
+
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -304,3 +304,44 @@ int of_device_uevent_modalias(const stru
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(of_device_uevent_modalias);
++
++/**
++ * of_device_make_bus_id - Use the device node data to assign a unique name
++ * @dev: pointer to device structure that is linked to a device tree node
++ *
++ * This routine will first try using the translated bus address to
++ * derive a unique name. If it cannot, then it will prepend names from
++ * parent nodes until a unique name can be derived.
++ */
++void of_device_make_bus_id(struct device *dev)
++{
++      struct device_node *node = dev->of_node;
++      const __be32 *reg;
++      u64 addr;
++      u32 mask;
++
++      /* Construct the name, using parent nodes if necessary to ensure uniqueness */
++      while (node->parent) {
++              /*
++               * If the address can be translated, then that is as much
++               * uniqueness as we need. Make it the first component and return
++               */
++              reg = of_get_property(node, "reg", NULL);
++              if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
++                      if (!of_property_read_u32(node, "mask", &mask))
++                              dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
++                                           addr, ffs(mask) - 1, node, dev_name(dev));
++
++                      else
++                              dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
++                                           addr, node, dev_name(dev));
++                      return;
++              }
++
++              /* format arguments only used if dev_name() resolves to NULL */
++              dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
++                           kbasename(node->full_name), dev_name(dev));
++              node = node->parent;
++      }
++}
++EXPORT_SYMBOL_GPL(of_device_make_bus_id);
+--- a/drivers/of/platform.c
++++ b/drivers/of/platform.c
+@@ -98,46 +98,6 @@ static const struct of_device_id of_skip
+  */
+ /**
+- * of_device_make_bus_id - Use the device node data to assign a unique name
+- * @dev: pointer to device structure that is linked to a device tree node
+- *
+- * This routine will first try using the translated bus address to
+- * derive a unique name. If it cannot, then it will prepend names from
+- * parent nodes until a unique name can be derived.
+- */
+-static void of_device_make_bus_id(struct device *dev)
+-{
+-      struct device_node *node = dev->of_node;
+-      const __be32 *reg;
+-      u64 addr;
+-      u32 mask;
+-
+-      /* Construct the name, using parent nodes if necessary to ensure uniqueness */
+-      while (node->parent) {
+-              /*
+-               * If the address can be translated, then that is as much
+-               * uniqueness as we need. Make it the first component and return
+-               */
+-              reg = of_get_property(node, "reg", NULL);
+-              if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
+-                      if (!of_property_read_u32(node, "mask", &mask))
+-                              dev_set_name(dev, dev_name(dev) ? "%llx.%x.%pOFn:%s" : "%llx.%x.%pOFn",
+-                                           addr, ffs(mask) - 1, node, dev_name(dev));
+-
+-                      else
+-                              dev_set_name(dev, dev_name(dev) ? "%llx.%pOFn:%s" : "%llx.%pOFn",
+-                                           addr, node, dev_name(dev));
+-                      return;
+-              }
+-
+-              /* format arguments only used if dev_name() resolves to NULL */
+-              dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
+-                           kbasename(node->full_name), dev_name(dev));
+-              node = node->parent;
+-      }
+-}
+-
+-/**
+  * of_device_alloc - Allocate and initialize an of_device
+  * @np: device node to assign to device
+  * @bus_id: Name to assign to the device.  May be null to use default name.
+--- a/include/linux/of_device.h
++++ b/include/linux/of_device.h
+@@ -40,6 +40,9 @@ static inline int of_dma_configure(struc
+ {
+       return of_dma_configure_id(dev, np, force_dma, NULL);
+ }
++
++void of_device_make_bus_id(struct device *dev);
++
+ #else /* CONFIG_OF */
+ static inline int of_driver_match_device(struct device *dev,
+@@ -82,6 +85,9 @@ static inline int of_dma_configure(struc
+ {
+       return 0;
+ }
++
++static inline void of_device_make_bus_id(struct device *dev) {}
++
+ #endif /* CONFIG_OF */
+ #endif /* _LINUX_OF_DEVICE_H */
diff --git a/target/linux/generic/backport-6.6/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch b/target/linux/generic/backport-6.6/819-v6.8-0001-nvmem-Move-of_nvmem_layout_get_container-in-another-.patch
new file mode 100644 (file)
index 0000000..0290d64
--- /dev/null
@@ -0,0 +1,95 @@
+From 4a1a40233b4a9fc159a5c7a27dc34c5c7bc5be55 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:28 +0000
+Subject: [PATCH] nvmem: Move of_nvmem_layout_get_container() in another header
+
+nvmem-consumer.h is included by consumer devices, extracting data from
+NVMEM devices whereas nvmem-provider.h is included by devices providing
+NVMEM content.
+
+The only users of of_nvmem_layout_get_container() outside of the core
+are layout drivers, so better move its prototype to nvmem-provider.h.
+
+While we do so, we also move the kdoc associated with the function to
+the header rather than the .c file.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c           |  8 --------
+ include/linux/nvmem-consumer.h |  7 -------
+ include/linux/nvmem-provider.h | 21 +++++++++++++++++++++
+ 3 files changed, 21 insertions(+), 15 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -846,14 +846,6 @@ static int nvmem_add_cells_from_layout(s
+ }
+ #if IS_ENABLED(CONFIG_OF)
+-/**
+- * of_nvmem_layout_get_container() - Get OF node to layout container.
+- *
+- * @nvmem: nvmem device.
+- *
+- * Return: a node pointer with refcount incremented or NULL if no
+- * container exists. Use of_node_put() on it when done.
+- */
+ struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+ {
+       return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
+--- a/include/linux/nvmem-consumer.h
++++ b/include/linux/nvmem-consumer.h
+@@ -242,7 +242,6 @@ struct nvmem_cell *of_nvmem_cell_get(str
+                                    const char *id);
+ struct nvmem_device *of_nvmem_device_get(struct device_node *np,
+                                        const char *name);
+-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
+ #else
+ static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
+                                                  const char *id)
+@@ -255,12 +254,6 @@ static inline struct nvmem_device *of_nv
+ {
+       return ERR_PTR(-EOPNOTSUPP);
+ }
+-
+-static inline struct device_node *
+-of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+-{
+-      return NULL;
+-}
+ #endif /* CONFIG_NVMEM && CONFIG_OF */
+ #endif  /* ifndef _LINUX_NVMEM_CONSUMER_H */
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -244,6 +244,27 @@ nvmem_layout_get_match_data(struct nvmem
+ #endif /* CONFIG_NVMEM */
++#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
++
++/**
++ * of_nvmem_layout_get_container() - Get OF node of layout container
++ *
++ * @nvmem: nvmem device
++ *
++ * Return: a node pointer with refcount incremented or NULL if no
++ * container exists. Use of_node_put() on it when done.
++ */
++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem);
++
++#else  /* CONFIG_NVMEM && CONFIG_OF */
++
++static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
++{
++      return NULL;
++}
++
++#endif /* CONFIG_NVMEM && CONFIG_OF */
++
+ #define module_nvmem_layout_driver(__layout_driver)           \
+       module_driver(__layout_driver, nvmem_layout_register,   \
+                     nvmem_layout_unregister)
diff --git a/target/linux/generic/backport-6.6/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch b/target/linux/generic/backport-6.6/819-v6.8-0002-nvmem-Create-a-header-for-internal-sharing.patch
new file mode 100644 (file)
index 0000000..b03ce68
--- /dev/null
@@ -0,0 +1,91 @@
+From ec9c08a1cb8dc5e8e003f95f5f62de41dde235bb Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:29 +0000
+Subject: [PATCH] nvmem: Create a header for internal sharing
+
+Before adding all the NVMEM layout bus infrastructure to the core, let's
+move the main nvmem_device structure in an internal header, only
+available to the core. This way all the additional code can be added in
+a dedicated file in order to keep the current core file tidy.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c      | 24 +-----------------------
+ drivers/nvmem/internals.h | 35 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 36 insertions(+), 23 deletions(-)
+ create mode 100644 drivers/nvmem/internals.h
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -19,29 +19,7 @@
+ #include <linux/of.h>
+ #include <linux/slab.h>
+-struct nvmem_device {
+-      struct module           *owner;
+-      struct device           dev;
+-      int                     stride;
+-      int                     word_size;
+-      int                     id;
+-      struct kref             refcnt;
+-      size_t                  size;
+-      bool                    read_only;
+-      bool                    root_only;
+-      int                     flags;
+-      enum nvmem_type         type;
+-      struct bin_attribute    eeprom;
+-      struct device           *base_dev;
+-      struct list_head        cells;
+-      const struct nvmem_keepout *keepout;
+-      unsigned int            nkeepout;
+-      nvmem_reg_read_t        reg_read;
+-      nvmem_reg_write_t       reg_write;
+-      struct gpio_desc        *wp_gpio;
+-      struct nvmem_layout     *layout;
+-      void *priv;
+-};
++#include "internals.h"
+ #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
+--- /dev/null
++++ b/drivers/nvmem/internals.h
+@@ -0,0 +1,35 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++
++#ifndef _LINUX_NVMEM_INTERNALS_H
++#define _LINUX_NVMEM_INTERNALS_H
++
++#include <linux/device.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++
++struct nvmem_device {
++      struct module           *owner;
++      struct device           dev;
++      struct list_head        node;
++      int                     stride;
++      int                     word_size;
++      int                     id;
++      struct kref             refcnt;
++      size_t                  size;
++      bool                    read_only;
++      bool                    root_only;
++      int                     flags;
++      enum nvmem_type         type;
++      struct bin_attribute    eeprom;
++      struct device           *base_dev;
++      struct list_head        cells;
++      const struct nvmem_keepout *keepout;
++      unsigned int            nkeepout;
++      nvmem_reg_read_t        reg_read;
++      nvmem_reg_write_t       reg_write;
++      struct gpio_desc        *wp_gpio;
++      struct nvmem_layout     *layout;
++      void *priv;
++};
++
++#endif  /* ifndef _LINUX_NVMEM_INTERNALS_H */
diff --git a/target/linux/generic/backport-6.6/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch b/target/linux/generic/backport-6.6/819-v6.8-0003-nvmem-Simplify-the-add_cells-hook.patch
new file mode 100644 (file)
index 0000000..dac691e
--- /dev/null
@@ -0,0 +1,79 @@
+From 1b7c298a4ecbc28cc6ee94005734bff55eb83d22 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:30 +0000
+Subject: [PATCH] nvmem: Simplify the ->add_cells() hook
+
+The layout entry is not used and will anyway be made useless by the new
+layout bus infrastructure coming next, so drop it. While at it, clarify
+the kdoc entry.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c             | 2 +-
+ drivers/nvmem/layouts/onie-tlv.c | 3 +--
+ drivers/nvmem/layouts/sl28vpd.c  | 3 +--
+ include/linux/nvmem-provider.h   | 8 +++-----
+ 4 files changed, 6 insertions(+), 10 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -815,7 +815,7 @@ static int nvmem_add_cells_from_layout(s
+       int ret;
+       if (layout && layout->add_cells) {
+-              ret = layout->add_cells(&nvmem->dev, nvmem, layout);
++              ret = layout->add_cells(&nvmem->dev, nvmem);
+               if (ret)
+                       return ret;
+       }
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -182,8 +182,7 @@ static bool onie_tlv_crc_is_valid(struct
+       return true;
+ }
+-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem,
+-                              struct nvmem_layout *layout)
++static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem)
+ {
+       struct onie_tlv_hdr hdr;
+       size_t table_len, data_len, hdr_len;
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -80,8 +80,7 @@ static int sl28vpd_v1_check_crc(struct d
+       return 0;
+ }
+-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem,
+-                           struct nvmem_layout *layout)
++static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem)
+ {
+       const struct nvmem_cell_info *pinfo;
+       struct nvmem_cell_info info = {0};
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -156,9 +156,8 @@ struct nvmem_cell_table {
+  *
+  * @name:             Layout name.
+  * @of_match_table:   Open firmware match table.
+- * @add_cells:                Will be called if a nvmem device is found which
+- *                    has this layout. The function will add layout
+- *                    specific cells with nvmem_add_one_cell().
++ * @add_cells:                Called to populate the layout using
++ *                    nvmem_add_one_cell().
+  * @fixup_cell_info:  Will be called before a cell is added. Can be
+  *                    used to modify the nvmem_cell_info.
+  * @owner:            Pointer to struct module.
+@@ -172,8 +171,7 @@ struct nvmem_cell_table {
+ struct nvmem_layout {
+       const char *name;
+       const struct of_device_id *of_match_table;
+-      int (*add_cells)(struct device *dev, struct nvmem_device *nvmem,
+-                       struct nvmem_layout *layout);
++      int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
+       void (*fixup_cell_info)(struct nvmem_device *nvmem,
+                               struct nvmem_layout *layout,
+                               struct nvmem_cell_info *cell);
diff --git a/target/linux/generic/backport-6.6/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch b/target/linux/generic/backport-6.6/819-v6.8-0004-nvmem-Move-and-rename-fixup_cell_info.patch
new file mode 100644 (file)
index 0000000..0a614fc
--- /dev/null
@@ -0,0 +1,169 @@
+From 1172460e716784ac7e1049a537bdca8edbf97360 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:31 +0000
+Subject: [PATCH] nvmem: Move and rename ->fixup_cell_info()
+
+This hook is meant to be used by any provider and instantiating a layout
+just for this is useless. Let's instead move this hook to the nvmem
+device and add it to the config structure to be easily shared by the
+providers.
+
+While at moving this hook, rename it ->fixup_dt_cell_info() to clarify
+its main intended purpose.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-6-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c           |  6 +++---
+ drivers/nvmem/imx-ocotp.c      | 11 +++--------
+ drivers/nvmem/internals.h      |  2 ++
+ drivers/nvmem/mtk-efuse.c      | 11 +++--------
+ include/linux/nvmem-provider.h |  9 ++++-----
+ 5 files changed, 15 insertions(+), 24 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -674,7 +674,6 @@ static int nvmem_validate_keepouts(struc
+ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
+ {
+-      struct nvmem_layout *layout = nvmem->layout;
+       struct device *dev = &nvmem->dev;
+       struct device_node *child;
+       const __be32 *addr;
+@@ -704,8 +703,8 @@ static int nvmem_add_cells_from_dt(struc
+               info.np = of_node_get(child);
+-              if (layout && layout->fixup_cell_info)
+-                      layout->fixup_cell_info(nvmem, layout, &info);
++              if (nvmem->fixup_dt_cell_info)
++                      nvmem->fixup_dt_cell_info(nvmem, &info);
+               ret = nvmem_add_one_cell(nvmem, &info);
+               kfree(info.name);
+@@ -894,6 +893,7 @@ struct nvmem_device *nvmem_register(cons
+       kref_init(&nvmem->refcnt);
+       INIT_LIST_HEAD(&nvmem->cells);
++      nvmem->fixup_dt_cell_info = config->fixup_dt_cell_info;
+       nvmem->owner = config->owner;
+       if (!nvmem->owner && config->dev->driver)
+--- a/drivers/nvmem/imx-ocotp.c
++++ b/drivers/nvmem/imx-ocotp.c
+@@ -583,17 +583,12 @@ static const struct of_device_id imx_oco
+ };
+ MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
+-static void imx_ocotp_fixup_cell_info(struct nvmem_device *nvmem,
+-                                    struct nvmem_layout *layout,
+-                                    struct nvmem_cell_info *cell)
++static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem,
++                                       struct nvmem_cell_info *cell)
+ {
+       cell->read_post_process = imx_ocotp_cell_pp;
+ }
+-static struct nvmem_layout imx_ocotp_layout = {
+-      .fixup_cell_info = imx_ocotp_fixup_cell_info,
+-};
+-
+ static int imx_ocotp_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+@@ -619,7 +614,7 @@ static int imx_ocotp_probe(struct platfo
+       imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
+       imx_ocotp_nvmem_config.dev = dev;
+       imx_ocotp_nvmem_config.priv = priv;
+-      imx_ocotp_nvmem_config.layout = &imx_ocotp_layout;
++      imx_ocotp_nvmem_config.fixup_dt_cell_info = &imx_ocotp_fixup_dt_cell_info;
+       priv->config = &imx_ocotp_nvmem_config;
+--- a/drivers/nvmem/internals.h
++++ b/drivers/nvmem/internals.h
+@@ -23,6 +23,8 @@ struct nvmem_device {
+       struct bin_attribute    eeprom;
+       struct device           *base_dev;
+       struct list_head        cells;
++      void (*fixup_dt_cell_info)(struct nvmem_device *nvmem,
++                                 struct nvmem_cell_info *cell);
+       const struct nvmem_keepout *keepout;
+       unsigned int            nkeepout;
+       nvmem_reg_read_t        reg_read;
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -45,9 +45,8 @@ static int mtk_efuse_gpu_speedbin_pp(voi
+       return 0;
+ }
+-static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem,
+-                                    struct nvmem_layout *layout,
+-                                    struct nvmem_cell_info *cell)
++static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem,
++                                       struct nvmem_cell_info *cell)
+ {
+       size_t sz = strlen(cell->name);
+@@ -61,10 +60,6 @@ static void mtk_efuse_fixup_cell_info(st
+               cell->read_post_process = mtk_efuse_gpu_speedbin_pp;
+ }
+-static struct nvmem_layout mtk_efuse_layout = {
+-      .fixup_cell_info = mtk_efuse_fixup_cell_info,
+-};
+-
+ static int mtk_efuse_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+@@ -91,7 +86,7 @@ static int mtk_efuse_probe(struct platfo
+       econfig.priv = priv;
+       econfig.dev = dev;
+       if (pdata->uses_post_processing)
+-              econfig.layout = &mtk_efuse_layout;
++              econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+       nvmem = devm_nvmem_register(dev, &econfig);
+       return PTR_ERR_OR_ZERO(nvmem);
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -83,6 +83,8 @@ struct nvmem_cell_info {
+  * @cells:    Optional array of pre-defined NVMEM cells.
+  * @ncells:   Number of elements in cells.
+  * @add_legacy_fixed_of_cells:        Read fixed NVMEM cells from old OF syntax.
++ * @fixup_dt_cell_info: Will be called before a cell is added. Can be
++ *            used to modify the nvmem_cell_info.
+  * @keepout:  Optional array of keepout ranges (sorted ascending by start).
+  * @nkeepout: Number of elements in the keepout array.
+  * @type:     Type of the nvmem storage
+@@ -113,6 +115,8 @@ struct nvmem_config {
+       const struct nvmem_cell_info    *cells;
+       int                     ncells;
+       bool                    add_legacy_fixed_of_cells;
++      void (*fixup_dt_cell_info)(struct nvmem_device *nvmem,
++                                 struct nvmem_cell_info *cell);
+       const struct nvmem_keepout *keepout;
+       unsigned int            nkeepout;
+       enum nvmem_type         type;
+@@ -158,8 +162,6 @@ struct nvmem_cell_table {
+  * @of_match_table:   Open firmware match table.
+  * @add_cells:                Called to populate the layout using
+  *                    nvmem_add_one_cell().
+- * @fixup_cell_info:  Will be called before a cell is added. Can be
+- *                    used to modify the nvmem_cell_info.
+  * @owner:            Pointer to struct module.
+  * @node:             List node.
+  *
+@@ -172,9 +174,6 @@ struct nvmem_layout {
+       const char *name;
+       const struct of_device_id *of_match_table;
+       int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
+-      void (*fixup_cell_info)(struct nvmem_device *nvmem,
+-                              struct nvmem_layout *layout,
+-                              struct nvmem_cell_info *cell);
+       /* private */
+       struct module *owner;
diff --git a/target/linux/generic/backport-6.6/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch b/target/linux/generic/backport-6.6/819-v6.8-0005-nvmem-core-Rework-layouts-to-become-regular-devices.patch
new file mode 100644 (file)
index 0000000..c846c48
--- /dev/null
@@ -0,0 +1,763 @@
+From fc29fd821d9ac2ae3d32a722fac39ce874efb883 Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:32 +0000
+Subject: [PATCH] nvmem: core: Rework layouts to become regular devices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Current layout support was initially written without modules support in
+mind. When the requirement for module support rose, the existing base
+was improved to adopt modularization support, but kind of a design flaw
+was introduced. With the existing implementation, when a storage device
+registers into NVMEM, the core tries to hook a layout (if any) and
+populates its cells immediately. This means, if the hardware description
+expects a layout to be hooked up, but no driver was provided for that,
+the storage medium will fail to probe and try later from
+scratch. Even if we consider that the hardware description shall be
+correct, we could still probe the storage device (especially if it
+contains the rootfs).
+
+One way to overcome this situation is to consider the layouts as
+devices, and leverage the native notifier mechanism. When a new NVMEM
+device is registered, we can populate its nvmem-layout child, if any,
+and wait for the matching to be done in order to get the cells (the
+waiting can be easily done with the NVMEM notifiers). If the layout
+driver is compiled as a module, it should automatically be loaded. This
+way, there is no strong order to enforce, any NVMEM device creation
+or NVMEM layout driver insertion will be observed as a new event which
+may lead to the creation of additional cells, without disturbing the
+probes with costly (and sometimes endless) deferrals.
+
+In order to achieve that goal we create a new bus for the nvmem-layouts
+with minimal logic to match nvmem-layout devices with nvmem-layout
+drivers. All this infrastructure code is created in the layouts.c file.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Tested-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/Kconfig            |   1 +
+ drivers/nvmem/Makefile           |   2 +
+ drivers/nvmem/core.c             | 170 ++++++++++----------------
+ drivers/nvmem/internals.h        |  21 ++++
+ drivers/nvmem/layouts.c          | 201 +++++++++++++++++++++++++++++++
+ drivers/nvmem/layouts/Kconfig    |   8 ++
+ drivers/nvmem/layouts/onie-tlv.c |  24 +++-
+ drivers/nvmem/layouts/sl28vpd.c  |  24 +++-
+ include/linux/nvmem-provider.h   |  38 +++---
+ 9 files changed, 354 insertions(+), 135 deletions(-)
+ create mode 100644 drivers/nvmem/layouts.c
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ menuconfig NVMEM
+       bool "NVMEM Support"
++      imply NVMEM_LAYOUTS
+       help
+         Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -5,6 +5,8 @@
+ obj-$(CONFIG_NVMEM)           += nvmem_core.o
+ nvmem_core-y                  := core.o
++obj-$(CONFIG_NVMEM_LAYOUTS)   += nvmem_layouts.o
++nvmem_layouts-y                       := layouts.o
+ obj-y                         += layouts/
+ # Devices
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -55,9 +55,6 @@ static LIST_HEAD(nvmem_lookup_list);
+ static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
+-static DEFINE_SPINLOCK(nvmem_layout_lock);
+-static LIST_HEAD(nvmem_layouts);
+-
+ static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
+                           void *val, size_t bytes)
+ {
+@@ -739,97 +736,22 @@ static int nvmem_add_cells_from_fixed_la
+       return err;
+ }
+-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
++int nvmem_layout_register(struct nvmem_layout *layout)
+ {
+-      layout->owner = owner;
+-
+-      spin_lock(&nvmem_layout_lock);
+-      list_add(&layout->node, &nvmem_layouts);
+-      spin_unlock(&nvmem_layout_lock);
+-
+-      blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout);
++      if (!layout->add_cells)
++              return -EINVAL;
+-      return 0;
++      /* Populate the cells */
++      return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
+ }
+-EXPORT_SYMBOL_GPL(__nvmem_layout_register);
++EXPORT_SYMBOL_GPL(nvmem_layout_register);
+ void nvmem_layout_unregister(struct nvmem_layout *layout)
+ {
+-      blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout);
+-
+-      spin_lock(&nvmem_layout_lock);
+-      list_del(&layout->node);
+-      spin_unlock(&nvmem_layout_lock);
++      /* Keep the API even with an empty stub in case we need it later */
+ }
+ EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
+-static struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
+-{
+-      struct device_node *layout_np;
+-      struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
+-
+-      layout_np = of_nvmem_layout_get_container(nvmem);
+-      if (!layout_np)
+-              return NULL;
+-
+-      /* Fixed layouts don't have a matching driver */
+-      if (of_device_is_compatible(layout_np, "fixed-layout")) {
+-              of_node_put(layout_np);
+-              return NULL;
+-      }
+-
+-      /*
+-       * In case the nvmem device was built-in while the layout was built as a
+-       * module, we shall manually request the layout driver loading otherwise
+-       * we'll never have any match.
+-       */
+-      of_request_module(layout_np);
+-
+-      spin_lock(&nvmem_layout_lock);
+-
+-      list_for_each_entry(l, &nvmem_layouts, node) {
+-              if (of_match_node(l->of_match_table, layout_np)) {
+-                      if (try_module_get(l->owner))
+-                              layout = l;
+-
+-                      break;
+-              }
+-      }
+-
+-      spin_unlock(&nvmem_layout_lock);
+-      of_node_put(layout_np);
+-
+-      return layout;
+-}
+-
+-static void nvmem_layout_put(struct nvmem_layout *layout)
+-{
+-      if (layout)
+-              module_put(layout->owner);
+-}
+-
+-static int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
+-{
+-      struct nvmem_layout *layout = nvmem->layout;
+-      int ret;
+-
+-      if (layout && layout->add_cells) {
+-              ret = layout->add_cells(&nvmem->dev, nvmem);
+-              if (ret)
+-                      return ret;
+-      }
+-
+-      return 0;
+-}
+-
+-#if IS_ENABLED(CONFIG_OF)
+-struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
+-{
+-      return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
+-}
+-EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
+-#endif
+-
+ const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+                                       struct nvmem_layout *layout)
+ {
+@@ -837,7 +759,7 @@ const void *nvmem_layout_get_match_data(
+       const struct of_device_id *match;
+       layout_np = of_nvmem_layout_get_container(nvmem);
+-      match = of_match_node(layout->of_match_table, layout_np);
++      match = of_match_node(layout->dev.driver->of_match_table, layout_np);
+       return match ? match->data : NULL;
+ }
+@@ -949,19 +871,6 @@ struct nvmem_device *nvmem_register(cons
+                       goto err_put_device;
+       }
+-      /*
+-       * If the driver supplied a layout by config->layout, the module
+-       * pointer will be NULL and nvmem_layout_put() will be a noop.
+-       */
+-      nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
+-      if (IS_ERR(nvmem->layout)) {
+-              rval = PTR_ERR(nvmem->layout);
+-              nvmem->layout = NULL;
+-
+-              if (rval == -EPROBE_DEFER)
+-                      goto err_teardown_compat;
+-      }
+-
+       if (config->cells) {
+               rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
+               if (rval)
+@@ -982,24 +891,24 @@ struct nvmem_device *nvmem_register(cons
+       if (rval)
+               goto err_remove_cells;
+-      rval = nvmem_add_cells_from_layout(nvmem);
+-      if (rval)
+-              goto err_remove_cells;
+-
+       dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
+       rval = device_add(&nvmem->dev);
+       if (rval)
+               goto err_remove_cells;
++      rval = nvmem_populate_layout(nvmem);
++      if (rval)
++              goto err_remove_dev;
++
+       blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
+       return nvmem;
++err_remove_dev:
++      device_del(&nvmem->dev);
+ err_remove_cells:
+       nvmem_device_remove_all_cells(nvmem);
+-      nvmem_layout_put(nvmem->layout);
+-err_teardown_compat:
+       if (config->compat)
+               nvmem_sysfs_remove_compat(nvmem, config);
+ err_put_device:
+@@ -1021,7 +930,7 @@ static void nvmem_device_release(struct
+               device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+       nvmem_device_remove_all_cells(nvmem);
+-      nvmem_layout_put(nvmem->layout);
++      nvmem_destroy_layout(nvmem);
+       device_unregister(&nvmem->dev);
+ }
+@@ -1323,6 +1232,12 @@ nvmem_cell_get_from_lookup(struct device
+       return cell;
+ }
++static void nvmem_layout_module_put(struct nvmem_device *nvmem)
++{
++      if (nvmem->layout && nvmem->layout->dev.driver)
++              module_put(nvmem->layout->dev.driver->owner);
++}
++
+ #if IS_ENABLED(CONFIG_OF)
+ static struct nvmem_cell_entry *
+ nvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np)
+@@ -1341,6 +1256,18 @@ nvmem_find_cell_entry_by_node(struct nvm
+       return cell;
+ }
++static int nvmem_layout_module_get_optional(struct nvmem_device *nvmem)
++{
++      if (!nvmem->layout)
++              return 0;
++
++      if (!nvmem->layout->dev.driver ||
++          !try_module_get(nvmem->layout->dev.driver->owner))
++              return -EPROBE_DEFER;
++
++      return 0;
++}
++
+ /**
+  * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
+  *
+@@ -1403,16 +1330,29 @@ struct nvmem_cell *of_nvmem_cell_get(str
+               return ERR_CAST(nvmem);
+       }
++      ret = nvmem_layout_module_get_optional(nvmem);
++      if (ret) {
++              of_node_put(cell_np);
++              __nvmem_device_put(nvmem);
++              return ERR_PTR(ret);
++      }
++
+       cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
+       of_node_put(cell_np);
+       if (!cell_entry) {
+               __nvmem_device_put(nvmem);
+-              return ERR_PTR(-ENOENT);
++              nvmem_layout_module_put(nvmem);
++              if (nvmem->layout)
++                      return ERR_PTR(-EPROBE_DEFER);
++              else
++                      return ERR_PTR(-ENOENT);
+       }
+       cell = nvmem_create_cell(cell_entry, id, cell_index);
+-      if (IS_ERR(cell))
++      if (IS_ERR(cell)) {
+               __nvmem_device_put(nvmem);
++              nvmem_layout_module_put(nvmem);
++      }
+       return cell;
+ }
+@@ -1526,6 +1466,7 @@ void nvmem_cell_put(struct nvmem_cell *c
+       kfree(cell);
+       __nvmem_device_put(nvmem);
++      nvmem_layout_module_put(nvmem);
+ }
+ EXPORT_SYMBOL_GPL(nvmem_cell_put);
+@@ -2116,11 +2057,22 @@ EXPORT_SYMBOL_GPL(nvmem_dev_size);
+ static int __init nvmem_init(void)
+ {
+-      return bus_register(&nvmem_bus_type);
++      int ret;
++
++      ret = bus_register(&nvmem_bus_type);
++      if (ret)
++              return ret;
++
++      ret = nvmem_layout_bus_register();
++      if (ret)
++              bus_unregister(&nvmem_bus_type);
++
++      return ret;
+ }
+ static void __exit nvmem_exit(void)
+ {
++      nvmem_layout_bus_unregister();
+       bus_unregister(&nvmem_bus_type);
+ }
+--- a/drivers/nvmem/internals.h
++++ b/drivers/nvmem/internals.h
+@@ -34,4 +34,25 @@ struct nvmem_device {
+       void *priv;
+ };
++#if IS_ENABLED(CONFIG_OF)
++int nvmem_layout_bus_register(void);
++void nvmem_layout_bus_unregister(void);
++int nvmem_populate_layout(struct nvmem_device *nvmem);
++void nvmem_destroy_layout(struct nvmem_device *nvmem);
++#else /* CONFIG_OF */
++static inline int nvmem_layout_bus_register(void)
++{
++      return 0;
++}
++
++static inline void nvmem_layout_bus_unregister(void) {}
++
++static inline int nvmem_populate_layout(struct nvmem_device *nvmem)
++{
++      return 0;
++}
++
++static inline void nvmem_destroy_layout(struct nvmem_device *nvmem) { }
++#endif /* CONFIG_OF */
++
+ #endif  /* ifndef _LINUX_NVMEM_INTERNALS_H */
+--- /dev/null
++++ b/drivers/nvmem/layouts.c
+@@ -0,0 +1,201 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * NVMEM layout bus handling
++ *
++ * Copyright (C) 2023 Bootlin
++ * Author: Miquel Raynal <miquel.raynal@bootlin.com
++ */
++
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_irq.h>
++
++#include "internals.h"
++
++#define to_nvmem_layout_driver(drv) \
++      (container_of((drv), struct nvmem_layout_driver, driver))
++#define to_nvmem_layout_device(_dev) \
++      container_of((_dev), struct nvmem_layout, dev)
++
++static int nvmem_layout_bus_match(struct device *dev, struct device_driver *drv)
++{
++      return of_driver_match_device(dev, drv);
++}
++
++static int nvmem_layout_bus_probe(struct device *dev)
++{
++      struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
++      struct nvmem_layout *layout = to_nvmem_layout_device(dev);
++
++      if (!drv->probe || !drv->remove)
++              return -EINVAL;
++
++      return drv->probe(layout);
++}
++
++static void nvmem_layout_bus_remove(struct device *dev)
++{
++      struct nvmem_layout_driver *drv = to_nvmem_layout_driver(dev->driver);
++      struct nvmem_layout *layout = to_nvmem_layout_device(dev);
++
++      return drv->remove(layout);
++}
++
++static struct bus_type nvmem_layout_bus_type = {
++      .name           = "nvmem-layout",
++      .match          = nvmem_layout_bus_match,
++      .probe          = nvmem_layout_bus_probe,
++      .remove         = nvmem_layout_bus_remove,
++};
++
++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv)
++{
++      drv->driver.bus = &nvmem_layout_bus_type;
++
++      return driver_register(&drv->driver);
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_driver_register);
++
++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv)
++{
++      driver_unregister(&drv->driver);
++}
++EXPORT_SYMBOL_GPL(nvmem_layout_driver_unregister);
++
++static void nvmem_layout_release_device(struct device *dev)
++{
++      struct nvmem_layout *layout = to_nvmem_layout_device(dev);
++
++      of_node_put(layout->dev.of_node);
++      kfree(layout);
++}
++
++static int nvmem_layout_create_device(struct nvmem_device *nvmem,
++                                    struct device_node *np)
++{
++      struct nvmem_layout *layout;
++      struct device *dev;
++      int ret;
++
++      layout = kzalloc(sizeof(*layout), GFP_KERNEL);
++      if (!layout)
++              return -ENOMEM;
++
++      /* Create a bidirectional link */
++      layout->nvmem = nvmem;
++      nvmem->layout = layout;
++
++      /* Device model registration */
++      dev = &layout->dev;
++      device_initialize(dev);
++      dev->parent = &nvmem->dev;
++      dev->bus = &nvmem_layout_bus_type;
++      dev->release = nvmem_layout_release_device;
++      dev->coherent_dma_mask = DMA_BIT_MASK(32);
++      dev->dma_mask = &dev->coherent_dma_mask;
++      device_set_node(dev, of_fwnode_handle(of_node_get(np)));
++      of_device_make_bus_id(dev);
++      of_msi_configure(dev, dev->of_node);
++
++      ret = device_add(dev);
++      if (ret) {
++              put_device(dev);
++              return ret;
++      }
++
++      return 0;
++}
++
++static const struct of_device_id of_nvmem_layout_skip_table[] = {
++      { .compatible = "fixed-layout", },
++      {}
++};
++
++static int nvmem_layout_bus_populate(struct nvmem_device *nvmem,
++                                   struct device_node *layout_dn)
++{
++      int ret;
++
++      /* Make sure it has a compatible property */
++      if (!of_get_property(layout_dn, "compatible", NULL)) {
++              pr_debug("%s() - skipping %pOF, no compatible prop\n",
++                       __func__, layout_dn);
++              return 0;
++      }
++
++      /* Fixed layouts are parsed manually somewhere else for now */
++      if (of_match_node(of_nvmem_layout_skip_table, layout_dn)) {
++              pr_debug("%s() - skipping %pOF node\n", __func__, layout_dn);
++              return 0;
++      }
++
++      if (of_node_check_flag(layout_dn, OF_POPULATED_BUS)) {
++              pr_debug("%s() - skipping %pOF, already populated\n",
++                       __func__, layout_dn);
++
++              return 0;
++      }
++
++      /* NVMEM layout buses expect only a single device representing the layout */
++      ret = nvmem_layout_create_device(nvmem, layout_dn);
++      if (ret)
++              return ret;
++
++      of_node_set_flag(layout_dn, OF_POPULATED_BUS);
++
++      return 0;
++}
++
++struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
++{
++      return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
++}
++EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
++
++/*
++ * Returns the number of devices populated, 0 if the operation was not relevant
++ * for this nvmem device, an error code otherwise.
++ */
++int nvmem_populate_layout(struct nvmem_device *nvmem)
++{
++      struct device_node *layout_dn;
++      int ret;
++
++      layout_dn = of_nvmem_layout_get_container(nvmem);
++      if (!layout_dn)
++              return 0;
++
++      /* Populate the layout device */
++      device_links_supplier_sync_state_pause();
++      ret = nvmem_layout_bus_populate(nvmem, layout_dn);
++      device_links_supplier_sync_state_resume();
++
++      of_node_put(layout_dn);
++      return ret;
++}
++
++void nvmem_destroy_layout(struct nvmem_device *nvmem)
++{
++      struct device *dev;
++
++      if (!nvmem->layout)
++              return;
++
++      dev = &nvmem->layout->dev;
++      of_node_clear_flag(dev->of_node, OF_POPULATED_BUS);
++      device_unregister(dev);
++}
++
++int nvmem_layout_bus_register(void)
++{
++      return bus_register(&nvmem_layout_bus_type);
++}
++
++void nvmem_layout_bus_unregister(void)
++{
++      bus_unregister(&nvmem_layout_bus_type);
++}
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -1,5 +1,11 @@
+ # SPDX-License-Identifier: GPL-2.0
++config NVMEM_LAYOUTS
++      bool
++      depends on OF
++
++if NVMEM_LAYOUTS
++
+ menu "Layout Types"
+ config NVMEM_LAYOUT_SL28_VPD
+@@ -21,3 +27,5 @@ config NVMEM_LAYOUT_ONIE_TLV
+         If unsure, say N.
+ endmenu
++
++endif
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -225,16 +225,32 @@ static int onie_tlv_parse_table(struct d
+       return 0;
+ }
++static int onie_tlv_probe(struct nvmem_layout *layout)
++{
++      layout->add_cells = onie_tlv_parse_table;
++
++      return nvmem_layout_register(layout);
++}
++
++static void onie_tlv_remove(struct nvmem_layout *layout)
++{
++      nvmem_layout_unregister(layout);
++}
++
+ static const struct of_device_id onie_tlv_of_match_table[] = {
+       { .compatible = "onie,tlv-layout", },
+       {},
+ };
+ MODULE_DEVICE_TABLE(of, onie_tlv_of_match_table);
+-static struct nvmem_layout onie_tlv_layout = {
+-      .name = "ONIE tlv layout",
+-      .of_match_table = onie_tlv_of_match_table,
+-      .add_cells = onie_tlv_parse_table,
++static struct nvmem_layout_driver onie_tlv_layout = {
++      .driver = {
++              .owner = THIS_MODULE,
++              .name = "onie-tlv-layout",
++              .of_match_table = onie_tlv_of_match_table,
++      },
++      .probe = onie_tlv_probe,
++      .remove = onie_tlv_remove,
+ };
+ module_nvmem_layout_driver(onie_tlv_layout);
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -134,16 +134,32 @@ static int sl28vpd_add_cells(struct devi
+       return 0;
+ }
++static int sl28vpd_probe(struct nvmem_layout *layout)
++{
++      layout->add_cells = sl28vpd_add_cells;
++
++      return nvmem_layout_register(layout);
++}
++
++static void sl28vpd_remove(struct nvmem_layout *layout)
++{
++      nvmem_layout_unregister(layout);
++}
++
+ static const struct of_device_id sl28vpd_of_match_table[] = {
+       { .compatible = "kontron,sl28-vpd" },
+       {},
+ };
+ MODULE_DEVICE_TABLE(of, sl28vpd_of_match_table);
+-static struct nvmem_layout sl28vpd_layout = {
+-      .name = "sl28-vpd",
+-      .of_match_table = sl28vpd_of_match_table,
+-      .add_cells = sl28vpd_add_cells,
++static struct nvmem_layout_driver sl28vpd_layout = {
++      .driver = {
++              .owner = THIS_MODULE,
++              .name = "kontron-sl28vpd-layout",
++              .of_match_table = sl28vpd_of_match_table,
++      },
++      .probe = sl28vpd_probe,
++      .remove = sl28vpd_remove,
+ };
+ module_nvmem_layout_driver(sl28vpd_layout);
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -9,6 +9,7 @@
+ #ifndef _LINUX_NVMEM_PROVIDER_H
+ #define _LINUX_NVMEM_PROVIDER_H
++#include <linux/device.h>
+ #include <linux/device/driver.h>
+ #include <linux/err.h>
+ #include <linux/errno.h>
+@@ -158,12 +159,11 @@ struct nvmem_cell_table {
+ /**
+  * struct nvmem_layout - NVMEM layout definitions
+  *
+- * @name:             Layout name.
+- * @of_match_table:   Open firmware match table.
+- * @add_cells:                Called to populate the layout using
+- *                    nvmem_add_one_cell().
+- * @owner:            Pointer to struct module.
+- * @node:             List node.
++ * @dev:              Device-model layout device.
++ * @nvmem:            The underlying NVMEM device
++ * @add_cells:                Will be called if a nvmem device is found which
++ *                    has this layout. The function will add layout
++ *                    specific cells with nvmem_add_one_cell().
+  *
+  * A nvmem device can hold a well defined structure which can just be
+  * evaluated during runtime. For example a TLV list, or a list of "name=val"
+@@ -171,13 +171,15 @@ struct nvmem_cell_table {
+  * cells.
+  */
+ struct nvmem_layout {
+-      const char *name;
+-      const struct of_device_id *of_match_table;
++      struct device dev;
++      struct nvmem_device *nvmem;
+       int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
++};
+-      /* private */
+-      struct module *owner;
+-      struct list_head node;
++struct nvmem_layout_driver {
++      struct device_driver driver;
++      int (*probe)(struct nvmem_layout *layout);
++      void (*remove)(struct nvmem_layout *layout);
+ };
+ #if IS_ENABLED(CONFIG_NVMEM)
+@@ -194,11 +196,15 @@ void nvmem_del_cell_table(struct nvmem_c
+ int nvmem_add_one_cell(struct nvmem_device *nvmem,
+                      const struct nvmem_cell_info *info);
+-int __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner);
+-#define nvmem_layout_register(layout) \
+-      __nvmem_layout_register(layout, THIS_MODULE)
++int nvmem_layout_register(struct nvmem_layout *layout);
+ void nvmem_layout_unregister(struct nvmem_layout *layout);
++int nvmem_layout_driver_register(struct nvmem_layout_driver *drv);
++void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv);
++#define module_nvmem_layout_driver(__nvmem_layout_driver)             \
++      module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
++                    nvmem_layout_driver_unregister)
++
+ const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+                                       struct nvmem_layout *layout);
+@@ -262,8 +268,4 @@ static inline struct device_node *of_nvm
+ #endif /* CONFIG_NVMEM && CONFIG_OF */
+-#define module_nvmem_layout_driver(__layout_driver)           \
+-      module_driver(__layout_driver, nvmem_layout_register,   \
+-                    nvmem_layout_unregister)
+-
+ #endif  /* ifndef _LINUX_NVMEM_PROVIDER_H */
diff --git a/target/linux/generic/backport-6.6/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch b/target/linux/generic/backport-6.6/819-v6.8-0006-nvmem-core-Expose-cells-through-sysfs.patch
new file mode 100644 (file)
index 0000000..8442636
--- /dev/null
@@ -0,0 +1,240 @@
+From 0331c611949fffdf486652450901a4dc52bc5cca Mon Sep 17 00:00:00 2001
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+Date: Fri, 15 Dec 2023 11:15:34 +0000
+Subject: [PATCH] nvmem: core: Expose cells through sysfs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The binary content of nvmem devices is available to the user so in the
+easiest cases, finding the content of a cell is rather easy as it is
+just a matter of looking at a known and fixed offset. However, nvmem
+layouts have been recently introduced to cope with more advanced
+situations, where the offset and size of the cells is not known in
+advance or is dynamic. When using layouts, more advanced parsers are
+used by the kernel in order to give direct access to the content of each
+cell, regardless of its position/size in the underlying
+device. Unfortunately, these information are not accessible by users,
+unless by fully re-implementing the parser logic in userland.
+
+Let's expose the cells and their content through sysfs to avoid these
+situations. Of course the relevant NVMEM sysfs Kconfig option must be
+enabled for this support to be available.
+
+Not all nvmem devices expose cells. Indeed, the .bin_attrs attribute
+group member will be filled at runtime only when relevant and will
+remain empty otherwise. In this case, as the cells attribute group will
+be empty, it will not lead to any additional folder/file creation.
+
+Exposed cells are read-only. There is, in practice, everything in the
+core to support a write path, but as I don't see any need for that, I
+prefer to keep the interface simple (and probably safer). The interface
+is documented as being in the "testing" state which means we can later
+add a write attribute if though relevant.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Tested-by: Rafał Miłecki <rafal@milecki.pl>
+Tested-by: Chen-Yu Tsai <wenst@chromium.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-9-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c      | 135 +++++++++++++++++++++++++++++++++++++-
+ drivers/nvmem/internals.h |   1 +
+ 2 files changed, 135 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -299,6 +299,43 @@ static umode_t nvmem_bin_attr_is_visible
+       return nvmem_bin_attr_get_umode(nvmem);
+ }
++static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
++                                          const char *id, int index);
++
++static ssize_t nvmem_cell_attr_read(struct file *filp, struct kobject *kobj,
++                                  struct bin_attribute *attr, char *buf,
++                                  loff_t pos, size_t count)
++{
++      struct nvmem_cell_entry *entry;
++      struct nvmem_cell *cell = NULL;
++      size_t cell_sz, read_len;
++      void *content;
++
++      entry = attr->private;
++      cell = nvmem_create_cell(entry, entry->name, 0);
++      if (IS_ERR(cell))
++              return PTR_ERR(cell);
++
++      if (!cell)
++              return -EINVAL;
++
++      content = nvmem_cell_read(cell, &cell_sz);
++      if (IS_ERR(content)) {
++              read_len = PTR_ERR(content);
++              goto destroy_cell;
++      }
++
++      read_len = min_t(unsigned int, cell_sz - pos, count);
++      memcpy(buf, content + pos, read_len);
++      kfree(content);
++
++destroy_cell:
++      kfree_const(cell->id);
++      kfree(cell);
++
++      return read_len;
++}
++
+ /* default read/write permissions */
+ static struct bin_attribute bin_attr_rw_nvmem = {
+       .attr   = {
+@@ -320,11 +357,21 @@ static const struct attribute_group nvme
+       .is_bin_visible = nvmem_bin_attr_is_visible,
+ };
++/* Cell attributes will be dynamically allocated */
++static struct attribute_group nvmem_cells_group = {
++      .name           = "cells",
++};
++
+ static const struct attribute_group *nvmem_dev_groups[] = {
+       &nvmem_bin_group,
+       NULL,
+ };
++static const struct attribute_group *nvmem_cells_groups[] = {
++      &nvmem_cells_group,
++      NULL,
++};
++
+ static struct bin_attribute bin_attr_nvmem_eeprom_compat = {
+       .attr   = {
+               .name   = "eeprom",
+@@ -379,6 +426,68 @@ static void nvmem_sysfs_remove_compat(st
+               device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+ }
++static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
++{
++      struct bin_attribute **cells_attrs, *attrs;
++      struct nvmem_cell_entry *entry;
++      unsigned int ncells = 0, i = 0;
++      int ret = 0;
++
++      mutex_lock(&nvmem_mutex);
++
++      if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) {
++              nvmem_cells_group.bin_attrs = NULL;
++              goto unlock_mutex;
++      }
++
++      /* Allocate an array of attributes with a sentinel */
++      ncells = list_count_nodes(&nvmem->cells);
++      cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1,
++                                 sizeof(struct bin_attribute *), GFP_KERNEL);
++      if (!cells_attrs) {
++              ret = -ENOMEM;
++              goto unlock_mutex;
++      }
++
++      attrs = devm_kcalloc(&nvmem->dev, ncells, sizeof(struct bin_attribute), GFP_KERNEL);
++      if (!attrs) {
++              ret = -ENOMEM;
++              goto unlock_mutex;
++      }
++
++      /* Initialize each attribute to take the name and size of the cell */
++      list_for_each_entry(entry, &nvmem->cells, node) {
++              sysfs_bin_attr_init(&attrs[i]);
++              attrs[i].attr.name = devm_kasprintf(&nvmem->dev, GFP_KERNEL,
++                                                  "%s@%x", entry->name,
++                                                  entry->offset);
++              attrs[i].attr.mode = 0444;
++              attrs[i].size = entry->bytes;
++              attrs[i].read = &nvmem_cell_attr_read;
++              attrs[i].private = entry;
++              if (!attrs[i].attr.name) {
++                      ret = -ENOMEM;
++                      goto unlock_mutex;
++              }
++
++              cells_attrs[i] = &attrs[i];
++              i++;
++      }
++
++      nvmem_cells_group.bin_attrs = cells_attrs;
++
++      ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups);
++      if (ret)
++              goto unlock_mutex;
++
++      nvmem->sysfs_cells_populated = true;
++
++unlock_mutex:
++      mutex_unlock(&nvmem_mutex);
++
++      return ret;
++}
++
+ #else /* CONFIG_NVMEM_SYSFS */
+ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
+@@ -738,11 +847,25 @@ static int nvmem_add_cells_from_fixed_la
+ int nvmem_layout_register(struct nvmem_layout *layout)
+ {
++      int ret;
++
+       if (!layout->add_cells)
+               return -EINVAL;
+       /* Populate the cells */
+-      return layout->add_cells(&layout->nvmem->dev, layout->nvmem);
++      ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem);
++      if (ret)
++              return ret;
++
++#ifdef CONFIG_NVMEM_SYSFS
++      ret = nvmem_populate_sysfs_cells(layout->nvmem);
++      if (ret) {
++              nvmem_device_remove_all_cells(layout->nvmem);
++              return ret;
++      }
++#endif
++
++      return 0;
+ }
+ EXPORT_SYMBOL_GPL(nvmem_layout_register);
+@@ -901,10 +1024,20 @@ struct nvmem_device *nvmem_register(cons
+       if (rval)
+               goto err_remove_dev;
++#ifdef CONFIG_NVMEM_SYSFS
++      rval = nvmem_populate_sysfs_cells(nvmem);
++      if (rval)
++              goto err_destroy_layout;
++#endif
++
+       blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
+       return nvmem;
++#ifdef CONFIG_NVMEM_SYSFS
++err_destroy_layout:
++      nvmem_destroy_layout(nvmem);
++#endif
+ err_remove_dev:
+       device_del(&nvmem->dev);
+ err_remove_cells:
+--- a/drivers/nvmem/internals.h
++++ b/drivers/nvmem/internals.h
+@@ -32,6 +32,7 @@ struct nvmem_device {
+       struct gpio_desc        *wp_gpio;
+       struct nvmem_layout     *layout;
+       void *priv;
++      bool                    sysfs_cells_populated;
+ };
+ #if IS_ENABLED(CONFIG_OF)
diff --git a/target/linux/generic/backport-6.6/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch b/target/linux/generic/backport-6.6/819-v6.8-0007-nvmem-stm32-add-support-for-STM32MP25-BSEC-to-contro.patch
new file mode 100644 (file)
index 0000000..f686222
--- /dev/null
@@ -0,0 +1,65 @@
+From f0ac5b23039610619ca4a4805528553ecb6bc815 Mon Sep 17 00:00:00 2001
+From: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Date: Fri, 15 Dec 2023 11:15:36 +0000
+Subject: [PATCH] nvmem: stm32: add support for STM32MP25 BSEC to control OTP
+ data
+
+On STM32MP25, OTP area may be read/written by using BSEC (boot, security
+and OTP control). The BSEC internal peripheral is only managed by the
+secure world.
+
+The 12 Kbits of OTP (effective) are organized into the following regions:
+- lower OTP (OTP0 to OTP127) = 4096 lower OTP bits,
+  bitwise (1-bit) programmable
+- mid OTP (OTP128 to OTP255) = 4096 middle OTP bits,
+  bulk (32-bit) programmable
+- upper OTP (OTP256 to OTP383) = 4096 upper OTP bits,
+  bulk (32-bit) programmable,
+  only accessible when BSEC is in closed state.
+
+As HWKEY and ECIES key are only accessible by ROM code;
+only 368 OTP words are managed in this driver (OTP0 to OTP267).
+
+This patch adds the STM32MP25 configuration for reading and writing
+the OTP data using the OP-TEE BSEC TA services.
+
+Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20231215111536.316972-11-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/stm32-romem.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/nvmem/stm32-romem.c
++++ b/drivers/nvmem/stm32-romem.c
+@@ -269,6 +269,19 @@ static const struct stm32_romem_cfg stm3
+       .ta = true,
+ };
++/*
++ * STM32MP25 BSEC OTP: 3 regions of 32-bits data words
++ *   lower OTP (OTP0 to OTP127), bitwise (1-bit) programmable
++ *   mid OTP (OTP128 to OTP255), bulk (32-bit) programmable
++ *   upper OTP (OTP256 to OTP383), bulk (32-bit) programmable
++ *              but no access to HWKEY and ECIES key: limited at OTP367
++ */
++static const struct stm32_romem_cfg stm32mp25_bsec_cfg = {
++      .size = 368 * 4,
++      .lower = 127,
++      .ta = true,
++};
++
+ static const struct of_device_id stm32_romem_of_match[] __maybe_unused = {
+       { .compatible = "st,stm32f4-otp", }, {
+               .compatible = "st,stm32mp15-bsec",
+@@ -276,6 +289,9 @@ static const struct of_device_id stm32_r
+       }, {
+               .compatible = "st,stm32mp13-bsec",
+               .data = (void *)&stm32mp13_bsec_cfg,
++      }, {
++              .compatible = "st,stm32mp25-bsec",
++              .data = (void *)&stm32mp25_bsec_cfg,
+       },
+       { /* sentinel */ },
+ };
diff --git a/target/linux/generic/backport-6.6/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch b/target/linux/generic/backport-6.6/819-v6.8-0008-nvmem-layouts-refactor-.add_cells-callback-arguments.patch
new file mode 100644 (file)
index 0000000..a95770a
--- /dev/null
@@ -0,0 +1,94 @@
+From 401df0d4f4098ecc9c5278da2f50756d62e5b37d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 19 Dec 2023 13:01:03 +0100
+Subject: [PATCH] nvmem: layouts: refactor .add_cells() callback arguments
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Simply pass whole "struct nvmem_layout" instead of single variables.
+There is nothing in "struct nvmem_layout" that we have to hide from
+layout drivers. They also access it during .probe() and .remove().
+
+Thanks to this change:
+
+1. API gets more consistent
+   All layouts drivers callbacks get the same argument
+
+2. Layouts get correct device
+   Before this change NVMEM core code was passing NVMEM device instead
+   of layout device. That resulted in:
+   * Confusing prints
+   * Calling devm_*() helpers on wrong device
+   * Helpers like of_device_get_match_data() dereferencing NULLs
+
+3. It gets possible to get match data
+   First of all nvmem_layout_get_match_data() requires passing "struct
+   nvmem_layout" which .add_cells() callback didn't have before this. It
+   doesn't matter much as it's rather useless now anyway (and will be
+   dropped).
+   What's more important however is that of_device_get_match_data() can
+   be used now thanks to owning a proper device pointer.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Michael Walle <michael@walle.cc>
+Link: https://lore.kernel.org/r/20231219120104.3422-1-zajec5@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c             | 2 +-
+ drivers/nvmem/layouts/onie-tlv.c | 4 +++-
+ drivers/nvmem/layouts/sl28vpd.c  | 4 +++-
+ include/linux/nvmem-provider.h   | 2 +-
+ 4 files changed, 8 insertions(+), 4 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -853,7 +853,7 @@ int nvmem_layout_register(struct nvmem_l
+               return -EINVAL;
+       /* Populate the cells */
+-      ret = layout->add_cells(&layout->nvmem->dev, layout->nvmem);
++      ret = layout->add_cells(layout);
+       if (ret)
+               return ret;
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -182,8 +182,10 @@ static bool onie_tlv_crc_is_valid(struct
+       return true;
+ }
+-static int onie_tlv_parse_table(struct device *dev, struct nvmem_device *nvmem)
++static int onie_tlv_parse_table(struct nvmem_layout *layout)
+ {
++      struct nvmem_device *nvmem = layout->nvmem;
++      struct device *dev = &layout->dev;
+       struct onie_tlv_hdr hdr;
+       size_t table_len, data_len, hdr_len;
+       u8 *table, *data;
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -80,8 +80,10 @@ static int sl28vpd_v1_check_crc(struct d
+       return 0;
+ }
+-static int sl28vpd_add_cells(struct device *dev, struct nvmem_device *nvmem)
++static int sl28vpd_add_cells(struct nvmem_layout *layout)
+ {
++      struct nvmem_device *nvmem = layout->nvmem;
++      struct device *dev = &layout->dev;
+       const struct nvmem_cell_info *pinfo;
+       struct nvmem_cell_info info = {0};
+       struct device_node *layout_np;
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -173,7 +173,7 @@ struct nvmem_cell_table {
+ struct nvmem_layout {
+       struct device dev;
+       struct nvmem_device *nvmem;
+-      int (*add_cells)(struct device *dev, struct nvmem_device *nvmem);
++      int (*add_cells)(struct nvmem_layout *layout);
+ };
+ struct nvmem_layout_driver {
diff --git a/target/linux/generic/backport-6.6/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch b/target/linux/generic/backport-6.6/819-v6.8-0009-nvmem-drop-nvmem_layout_get_match_data.patch
new file mode 100644 (file)
index 0000000..291854b
--- /dev/null
@@ -0,0 +1,72 @@
+From 43f60e3fb62edc7bd8891de8779fb422f4ae23ae Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 19 Dec 2023 13:01:04 +0100
+Subject: [PATCH] nvmem: drop nvmem_layout_get_match_data()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Thanks for layouts refactoring we now have "struct device" associated
+with layout. Also its OF pointer points directly to the "nvmem-layout"
+DT node.
+
+All it takes to get match data is a generic of_device_get_match_data().
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Michael Walle <michael@walle.cc>
+Link: https://lore.kernel.org/r/20231219120104.3422-2-zajec5@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c           | 13 -------------
+ include/linux/nvmem-provider.h | 10 ----------
+ 2 files changed, 23 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -875,19 +875,6 @@ void nvmem_layout_unregister(struct nvme
+ }
+ EXPORT_SYMBOL_GPL(nvmem_layout_unregister);
+-const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+-                                      struct nvmem_layout *layout)
+-{
+-      struct device_node __maybe_unused *layout_np;
+-      const struct of_device_id *match;
+-
+-      layout_np = of_nvmem_layout_get_container(nvmem);
+-      match = of_match_node(layout->dev.driver->of_match_table, layout_np);
+-
+-      return match ? match->data : NULL;
+-}
+-EXPORT_SYMBOL_GPL(nvmem_layout_get_match_data);
+-
+ /**
+  * nvmem_register() - Register a nvmem device for given nvmem_config.
+  * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -205,9 +205,6 @@ void nvmem_layout_driver_unregister(stru
+       module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
+                     nvmem_layout_driver_unregister)
+-const void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+-                                      struct nvmem_layout *layout);
+-
+ #else
+ static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
+@@ -238,13 +235,6 @@ static inline int nvmem_layout_register(
+ static inline void nvmem_layout_unregister(struct nvmem_layout *layout) {}
+-static inline const void *
+-nvmem_layout_get_match_data(struct nvmem_device *nvmem,
+-                          struct nvmem_layout *layout)
+-{
+-      return NULL;
+-}
+-
+ #endif /* CONFIG_NVMEM */
+ #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
diff --git a/target/linux/generic/backport-6.6/820-v6.9-0002-nvmem-mtk-efuse-Register-MediaTek-socinfo-driver-fro.patch b/target/linux/generic/backport-6.6/820-v6.9-0002-nvmem-mtk-efuse-Register-MediaTek-socinfo-driver-fro.patch
new file mode 100644 (file)
index 0000000..8e7c823
--- /dev/null
@@ -0,0 +1,69 @@
+From 998f0633773b3432829fe45d2cd2ffb842f3c78e Mon Sep 17 00:00:00 2001
+From: William-tw Lin <william-tw.lin@mediatek.com>
+Date: Sat, 24 Feb 2024 11:45:07 +0000
+Subject: [PATCH] nvmem: mtk-efuse: Register MediaTek socinfo driver from efuse
+
+The socinfo driver reads chip information from eFuses and does not need
+any devicetree node. Register it from mtk-efuse.
+
+While at it, also add the name for this driver's nvmem_config.
+
+Signed-off-by: William-tw Lin <william-tw.lin@mediatek.com>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 21 ++++++++++++++++++++-
+ 1 file changed, 20 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -68,6 +68,7 @@ static int mtk_efuse_probe(struct platfo
+       struct nvmem_config econfig = {};
+       struct mtk_efuse_priv *priv;
+       const struct mtk_efuse_pdata *pdata;
++      struct platform_device *socinfo;
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+@@ -85,11 +86,20 @@ static int mtk_efuse_probe(struct platfo
+       econfig.size = resource_size(res);
+       econfig.priv = priv;
+       econfig.dev = dev;
++      econfig.name = "mtk-efuse";
+       if (pdata->uses_post_processing)
+               econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+       nvmem = devm_nvmem_register(dev, &econfig);
++      if (IS_ERR(nvmem))
++              return PTR_ERR(nvmem);
+-      return PTR_ERR_OR_ZERO(nvmem);
++      socinfo = platform_device_register_data(&pdev->dev, "mtk-socinfo",
++                                              PLATFORM_DEVID_AUTO, NULL, 0);
++      if (IS_ERR(socinfo))
++              dev_info(dev, "MediaTek SoC Information will be unavailable\n");
++
++      platform_set_drvdata(pdev, socinfo);
++      return 0;
+ }
+ static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = {
+@@ -108,8 +118,17 @@ static const struct of_device_id mtk_efu
+ };
+ MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
++static void mtk_efuse_remove(struct platform_device *pdev)
++{
++      struct platform_device *socinfo = platform_get_drvdata(pdev);
++
++      if (!IS_ERR_OR_NULL(socinfo))
++              platform_device_unregister(socinfo);
++}
++
+ static struct platform_driver mtk_efuse_driver = {
+       .probe = mtk_efuse_probe,
++      .remove_new = mtk_efuse_remove,
+       .driver = {
+               .name = "mediatek,efuse",
+               .of_match_table = mtk_efuse_of_match,
diff --git a/target/linux/generic/backport-6.6/820-v6.9-0003-nvmem-zynqmp_nvmem-zynqmp_nvmem_probe-cleanup.patch b/target/linux/generic/backport-6.6/820-v6.9-0003-nvmem-zynqmp_nvmem-zynqmp_nvmem_probe-cleanup.patch
new file mode 100644 (file)
index 0000000..0f90e54
--- /dev/null
@@ -0,0 +1,97 @@
+From 29be47fcd6a06ea2e79eeeca6e69ad1e23254a69 Mon Sep 17 00:00:00 2001
+From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Date: Sat, 24 Feb 2024 11:45:11 +0000
+Subject: [PATCH] nvmem: zynqmp_nvmem: zynqmp_nvmem_probe cleanup
+
+- Remove static nvmem_config declaration
+- Remove zynqmp_nvmem_data
+
+Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/zynqmp_nvmem.c | 37 ++++++++++++------------------------
+ 1 file changed, 12 insertions(+), 25 deletions(-)
+
+--- a/drivers/nvmem/zynqmp_nvmem.c
++++ b/drivers/nvmem/zynqmp_nvmem.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+  * Copyright (C) 2019 Xilinx, Inc.
++ * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
+  */
+ #include <linux/module.h>
+@@ -11,36 +12,25 @@
+ #define SILICON_REVISION_MASK 0xF
+-struct zynqmp_nvmem_data {
+-      struct device *dev;
+-      struct nvmem_device *nvmem;
+-};
+ static int zynqmp_nvmem_read(void *context, unsigned int offset,
+                            void *val, size_t bytes)
+ {
++      struct device *dev = context;
+       int ret;
+-      int idcode, version;
+-      struct zynqmp_nvmem_data *priv = context;
++      int idcode;
++      int version;
+       ret = zynqmp_pm_get_chipid(&idcode, &version);
+       if (ret < 0)
+               return ret;
+-      dev_dbg(priv->dev, "Read chipid val %x %x\n", idcode, version);
++      dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
+       *(int *)val = version & SILICON_REVISION_MASK;
+       return 0;
+ }
+-static struct nvmem_config econfig = {
+-      .name = "zynqmp-nvmem",
+-      .owner = THIS_MODULE,
+-      .word_size = 1,
+-      .size = 1,
+-      .read_only = true,
+-};
+-
+ static const struct of_device_id zynqmp_nvmem_match[] = {
+       { .compatible = "xlnx,zynqmp-nvmem-fw", },
+       { /* sentinel */ },
+@@ -50,21 +40,18 @@ MODULE_DEVICE_TABLE(of, zynqmp_nvmem_mat
+ static int zynqmp_nvmem_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+-      struct zynqmp_nvmem_data *priv;
++      struct nvmem_config econfig = {};
+-      priv = devm_kzalloc(dev, sizeof(struct zynqmp_nvmem_data), GFP_KERNEL);
+-      if (!priv)
+-              return -ENOMEM;
+-
+-      priv->dev = dev;
++      econfig.name = "zynqmp-nvmem";
++      econfig.owner = THIS_MODULE;
++      econfig.word_size = 1;
++      econfig.size = 1;
+       econfig.dev = dev;
+       econfig.add_legacy_fixed_of_cells = true;
++      econfig.read_only = true;
+       econfig.reg_read = zynqmp_nvmem_read;
+-      econfig.priv = priv;
+-
+-      priv->nvmem = devm_nvmem_register(dev, &econfig);
+-      return PTR_ERR_OR_ZERO(priv->nvmem);
++      return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
+ }
+ static struct platform_driver zynqmp_nvmem_driver = {
diff --git a/target/linux/generic/backport-6.6/820-v6.9-0004-nvmem-zynqmp_nvmem-Add-support-to-access-efuse.patch b/target/linux/generic/backport-6.6/820-v6.9-0004-nvmem-zynqmp_nvmem-Add-support-to-access-efuse.patch
new file mode 100644 (file)
index 0000000..39c2f17
--- /dev/null
@@ -0,0 +1,243 @@
+From 737c0c8d07b5f671c0a33cec95965fcb2d2ea893 Mon Sep 17 00:00:00 2001
+From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Date: Sat, 24 Feb 2024 11:45:12 +0000
+Subject: [PATCH] nvmem: zynqmp_nvmem: Add support to access efuse
+
+Add support to read/write efuse memory map of ZynqMP.
+Below are the offsets of ZynqMP efuse memory map
+       0 - SOC version(read only)
+       0xC - 0xFC -ZynqMP specific purpose efuses
+       0x100 - 0x17F - Physical Unclonable Function(PUF)
+                efuses repurposed as user efuses
+
+Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/zynqmp_nvmem.c | 186 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 176 insertions(+), 10 deletions(-)
+
+--- a/drivers/nvmem/zynqmp_nvmem.c
++++ b/drivers/nvmem/zynqmp_nvmem.c
+@@ -4,6 +4,7 @@
+  * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
+  */
++#include <linux/dma-mapping.h>
+ #include <linux/module.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/of.h>
+@@ -11,24 +12,189 @@
+ #include <linux/firmware/xlnx-zynqmp.h>
+ #define SILICON_REVISION_MASK 0xF
++#define P_USER_0_64_UPPER_MASK        GENMASK(31, 16)
++#define P_USER_127_LOWER_4_BIT_MASK GENMASK(3, 0)
++#define WORD_INBYTES          4
++#define SOC_VER_SIZE          0x4
++#define EFUSE_MEMORY_SIZE     0x177
++#define UNUSED_SPACE          0x8
++#define ZYNQMP_NVMEM_SIZE     (SOC_VER_SIZE + UNUSED_SPACE + \
++                               EFUSE_MEMORY_SIZE)
++#define SOC_VERSION_OFFSET    0x0
++#define EFUSE_START_OFFSET    0xC
++#define EFUSE_END_OFFSET      0xFC
++#define EFUSE_PUF_START_OFFSET        0x100
++#define EFUSE_PUF_MID_OFFSET  0x140
++#define EFUSE_PUF_END_OFFSET  0x17F
++#define EFUSE_NOT_ENABLED     29
++/*
++ * efuse access type
++ */
++enum efuse_access {
++      EFUSE_READ = 0,
++      EFUSE_WRITE
++};
++
++/**
++ * struct xilinx_efuse - the basic structure
++ * @src:      address of the buffer to store the data to be write/read
++ * @size:     read/write word count
++ * @offset:   read/write offset
++ * @flag:     0 - represents efuse read and 1- represents efuse write
++ * @pufuserfuse:0 - represents non-puf efuses, offset is used for read/write
++ *            1 - represents puf user fuse row number.
++ *
++ * this structure stores all the required details to
++ * read/write efuse memory.
++ */
++struct xilinx_efuse {
++      u64 src;
++      u32 size;
++      u32 offset;
++      enum efuse_access flag;
++      u32 pufuserfuse;
++};
++
++static int zynqmp_efuse_access(void *context, unsigned int offset,
++                             void *val, size_t bytes, enum efuse_access flag,
++                             unsigned int pufflag)
++{
++      struct device *dev = context;
++      struct xilinx_efuse *efuse;
++      dma_addr_t dma_addr;
++      dma_addr_t dma_buf;
++      size_t words = bytes / WORD_INBYTES;
++      int ret;
++      int value;
++      char *data;
+-static int zynqmp_nvmem_read(void *context, unsigned int offset,
+-                           void *val, size_t bytes)
++      if (bytes % WORD_INBYTES != 0) {
++              dev_err(dev, "Bytes requested should be word aligned\n");
++              return -EOPNOTSUPP;
++      }
++
++      if (pufflag == 0 && offset % WORD_INBYTES) {
++              dev_err(dev, "Offset requested should be word aligned\n");
++              return -EOPNOTSUPP;
++      }
++
++      if (pufflag == 1 && flag == EFUSE_WRITE) {
++              memcpy(&value, val, bytes);
++              if ((offset == EFUSE_PUF_START_OFFSET ||
++                   offset == EFUSE_PUF_MID_OFFSET) &&
++                  value & P_USER_0_64_UPPER_MASK) {
++                      dev_err(dev, "Only lower 4 bytes are allowed to be programmed in P_USER_0 & P_USER_64\n");
++                      return -EOPNOTSUPP;
++              }
++
++              if (offset == EFUSE_PUF_END_OFFSET &&
++                  (value & P_USER_127_LOWER_4_BIT_MASK)) {
++                      dev_err(dev, "Only MSB 28 bits are allowed to be programmed for P_USER_127\n");
++                      return -EOPNOTSUPP;
++              }
++      }
++
++      efuse = dma_alloc_coherent(dev, sizeof(struct xilinx_efuse),
++                                 &dma_addr, GFP_KERNEL);
++      if (!efuse)
++              return -ENOMEM;
++
++      data = dma_alloc_coherent(dev, sizeof(bytes),
++                                &dma_buf, GFP_KERNEL);
++      if (!data) {
++              ret = -ENOMEM;
++              goto efuse_data_fail;
++      }
++
++      if (flag == EFUSE_WRITE) {
++              memcpy(data, val, bytes);
++              efuse->flag = EFUSE_WRITE;
++      } else {
++              efuse->flag = EFUSE_READ;
++      }
++
++      efuse->src = dma_buf;
++      efuse->size = words;
++      efuse->offset = offset;
++      efuse->pufuserfuse = pufflag;
++
++      zynqmp_pm_efuse_access(dma_addr, (u32 *)&ret);
++      if (ret != 0) {
++              if (ret == EFUSE_NOT_ENABLED) {
++                      dev_err(dev, "efuse access is not enabled\n");
++                      ret = -EOPNOTSUPP;
++              } else {
++                      dev_err(dev, "Error in efuse read %x\n", ret);
++                      ret = -EPERM;
++              }
++              goto efuse_access_err;
++      }
++
++      if (flag == EFUSE_READ)
++              memcpy(val, data, bytes);
++efuse_access_err:
++      dma_free_coherent(dev, sizeof(bytes),
++                        data, dma_buf);
++efuse_data_fail:
++      dma_free_coherent(dev, sizeof(struct xilinx_efuse),
++                        efuse, dma_addr);
++
++      return ret;
++}
++
++static int zynqmp_nvmem_read(void *context, unsigned int offset, void *val, size_t bytes)
+ {
+       struct device *dev = context;
+       int ret;
++      int pufflag = 0;
+       int idcode;
+       int version;
+-      ret = zynqmp_pm_get_chipid(&idcode, &version);
+-      if (ret < 0)
+-              return ret;
++      if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
++              pufflag = 1;
++
++      switch (offset) {
++      /* Soc version offset is zero */
++      case SOC_VERSION_OFFSET:
++              if (bytes != SOC_VER_SIZE)
++                      return -EOPNOTSUPP;
++
++              ret = zynqmp_pm_get_chipid((u32 *)&idcode, (u32 *)&version);
++              if (ret < 0)
++                      return ret;
++
++              dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
++              *(int *)val = version & SILICON_REVISION_MASK;
++              break;
++      /* Efuse offset starts from 0xc */
++      case EFUSE_START_OFFSET ... EFUSE_END_OFFSET:
++      case EFUSE_PUF_START_OFFSET ... EFUSE_PUF_END_OFFSET:
++              ret = zynqmp_efuse_access(context, offset, val,
++                                        bytes, EFUSE_READ, pufflag);
++              break;
++      default:
++              *(u32 *)val = 0xDEADBEEF;
++              ret = 0;
++              break;
++      }
++
++      return ret;
++}
++
++static int zynqmp_nvmem_write(void *context,
++                            unsigned int offset, void *val, size_t bytes)
++{
++      int pufflag = 0;
++
++      if (offset < EFUSE_START_OFFSET || offset > EFUSE_PUF_END_OFFSET)
++              return -EOPNOTSUPP;
+-      dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
+-      *(int *)val = version & SILICON_REVISION_MASK;
++      if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
++              pufflag = 1;
+-      return 0;
++      return zynqmp_efuse_access(context, offset,
++                                 val, bytes, EFUSE_WRITE, pufflag);
+ }
+ static const struct of_device_id zynqmp_nvmem_match[] = {
+@@ -45,11 +211,11 @@ static int zynqmp_nvmem_probe(struct pla
+       econfig.name = "zynqmp-nvmem";
+       econfig.owner = THIS_MODULE;
+       econfig.word_size = 1;
+-      econfig.size = 1;
++      econfig.size = ZYNQMP_NVMEM_SIZE;
+       econfig.dev = dev;
+       econfig.add_legacy_fixed_of_cells = true;
+-      econfig.read_only = true;
+       econfig.reg_read = zynqmp_nvmem_read;
++      econfig.reg_write = zynqmp_nvmem_write;
+       return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
+ }
diff --git a/target/linux/generic/backport-6.6/820-v6.9-0005-nvmem-mtk-efuse-Drop-NVMEM-device-name.patch b/target/linux/generic/backport-6.6/820-v6.9-0005-nvmem-mtk-efuse-Drop-NVMEM-device-name.patch
new file mode 100644 (file)
index 0000000..c67399c
--- /dev/null
@@ -0,0 +1,35 @@
+From 76c345edef754b16cab81ad9452cc49c09e67066 Mon Sep 17 00:00:00 2001
+From: Chen-Yu Tsai <wenst@chromium.org>
+Date: Sat, 24 Feb 2024 11:45:14 +0000
+Subject: [PATCH] nvmem: mtk-efuse: Drop NVMEM device name
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The MT8183 has not one but two efuse devices. The static name and ID
+causes the second efuse device to fail to probe, due to duplicate sysfs
+entries.
+
+With the rework of the mtk-socinfo driver, lookup by name is no longer
+necessary. The custom name can simply be dropped.
+
+Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Tested-by: "Nícolas F. R. A. Prado" <nfraprado@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-10-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -86,7 +86,6 @@ static int mtk_efuse_probe(struct platfo
+       econfig.size = resource_size(res);
+       econfig.priv = priv;
+       econfig.dev = dev;
+-      econfig.name = "mtk-efuse";
+       if (pdata->uses_post_processing)
+               econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+       nvmem = devm_nvmem_register(dev, &econfig);
diff --git a/target/linux/generic/backport-6.6/820-v6.9-0006-nvmem-core-make-nvmem_layout_bus_type-const.patch b/target/linux/generic/backport-6.6/820-v6.9-0006-nvmem-core-make-nvmem_layout_bus_type-const.patch
new file mode 100644 (file)
index 0000000..506092e
--- /dev/null
@@ -0,0 +1,33 @@
+From 8ec0faf2572216b4e25d6829cd41cf3ee2dab979 Mon Sep 17 00:00:00 2001
+From: "Ricardo B. Marliere" <ricardo@marliere.net>
+Date: Sat, 24 Feb 2024 11:45:15 +0000
+Subject: [PATCH] nvmem: core: make nvmem_layout_bus_type const
+
+Since commit d492cc2573a0 ("driver core: device.h: make struct bus_type
+a const *"), the driver core can properly handle constant struct
+bus_type, move the nvmem_layout_bus_type variable to be a constant
+structure as well, placing it into read-only memory which can not be
+modified at runtime.
+
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: "Ricardo B. Marliere" <ricardo@marliere.net>
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-11-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/layouts.c
++++ b/drivers/nvmem/layouts.c
+@@ -45,7 +45,7 @@ static void nvmem_layout_bus_remove(stru
+       return drv->remove(layout);
+ }
+-static struct bus_type nvmem_layout_bus_type = {
++static const struct bus_type nvmem_layout_bus_type = {
+       .name           = "nvmem-layout",
+       .match          = nvmem_layout_bus_match,
+       .probe          = nvmem_layout_bus_probe,
diff --git a/target/linux/generic/backport-6.6/820-v6.9-0007-nvmem-core-Print-error-on-wrong-bits-DT-property.patch b/target/linux/generic/backport-6.6/820-v6.9-0007-nvmem-core-Print-error-on-wrong-bits-DT-property.patch
new file mode 100644 (file)
index 0000000..aa4e0ab
--- /dev/null
@@ -0,0 +1,32 @@
+From def3173d4f17b37cecbd74d7c269a080b0b01598 Mon Sep 17 00:00:00 2001
+From: Markus Schneider-Pargmann <msp@baylibre.com>
+Date: Sat, 24 Feb 2024 11:45:16 +0000
+Subject: [PATCH] nvmem: core: Print error on wrong bits DT property
+
+The algorithms in nvmem core are built with the constraint that
+bit_offset < 8. If bit_offset is greater the results are wrong. Print an
+error if the devicetree 'bits' property is outside of the valid range
+and abort parsing.
+
+Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-12-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -805,6 +805,11 @@ static int nvmem_add_cells_from_dt(struc
+               if (addr && len == (2 * sizeof(u32))) {
+                       info.bit_offset = be32_to_cpup(addr++);
+                       info.nbits = be32_to_cpup(addr);
++                      if (info.bit_offset >= BITS_PER_BYTE || info.nbits < 1) {
++                              dev_err(dev, "nvmem: invalid bits on %pOF\n", child);
++                              of_node_put(child);
++                              return -EINVAL;
++                      }
+               }
+               info.np = of_node_get(child);
diff --git a/target/linux/generic/backport-6.6/821-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch b/target/linux/generic/backport-6.6/821-v6.10-0001-nvmem-layouts-store-owner-from-modules-with-nvmem_la.patch
new file mode 100644 (file)
index 0000000..418d2c8
--- /dev/null
@@ -0,0 +1,61 @@
+From 6d0ca4a2a7e25f9ad07c1f335f20b4d9e048cdd5 Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Date: Tue, 30 Apr 2024 09:49:11 +0100
+Subject: [PATCH] nvmem: layouts: store owner from modules with
+ nvmem_layout_driver_register()
+
+Modules registering driver with nvmem_layout_driver_register() might
+forget to set .owner field.  The field is used by some of other kernel
+parts for reference counting (try_module_get()), so it is expected that
+drivers will set it.
+
+Solve the problem by moving this task away from the drivers to the core
+code, just like we did for platform_driver in
+commit 9447057eaff8 ("platform_device: use a macro instead of
+platform_driver_register").
+
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Reviewed-by: Michael Walle <mwalle@kernel.org>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240430084921.33387-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts.c        | 6 ++++--
+ include/linux/nvmem-provider.h | 5 ++++-
+ 2 files changed, 8 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/layouts.c
++++ b/drivers/nvmem/layouts.c
+@@ -52,13 +52,15 @@ static const struct bus_type nvmem_layou
+       .remove         = nvmem_layout_bus_remove,
+ };
+-int nvmem_layout_driver_register(struct nvmem_layout_driver *drv)
++int __nvmem_layout_driver_register(struct nvmem_layout_driver *drv,
++                                 struct module *owner)
+ {
+       drv->driver.bus = &nvmem_layout_bus_type;
++      drv->driver.owner = owner;
+       return driver_register(&drv->driver);
+ }
+-EXPORT_SYMBOL_GPL(nvmem_layout_driver_register);
++EXPORT_SYMBOL_GPL(__nvmem_layout_driver_register);
+ void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv)
+ {
+--- a/include/linux/nvmem-provider.h
++++ b/include/linux/nvmem-provider.h
+@@ -199,7 +199,10 @@ int nvmem_add_one_cell(struct nvmem_devi
+ int nvmem_layout_register(struct nvmem_layout *layout);
+ void nvmem_layout_unregister(struct nvmem_layout *layout);
+-int nvmem_layout_driver_register(struct nvmem_layout_driver *drv);
++#define nvmem_layout_driver_register(drv) \
++      __nvmem_layout_driver_register(drv, THIS_MODULE)
++int __nvmem_layout_driver_register(struct nvmem_layout_driver *drv,
++                                 struct module *owner);
+ void nvmem_layout_driver_unregister(struct nvmem_layout_driver *drv);
+ #define module_nvmem_layout_driver(__nvmem_layout_driver)             \
+       module_driver(__nvmem_layout_driver, nvmem_layout_driver_register, \
diff --git a/target/linux/generic/backport-6.6/821-v6.10-0002-nvmem-layouts-onie-tlv-drop-driver-owner-initializat.patch b/target/linux/generic/backport-6.6/821-v6.10-0002-nvmem-layouts-onie-tlv-drop-driver-owner-initializat.patch
new file mode 100644 (file)
index 0000000..b483dd2
--- /dev/null
@@ -0,0 +1,28 @@
+From 21833338eccb91194fec6ba7548d9c454824eca0 Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Date: Tue, 30 Apr 2024 09:49:12 +0100
+Subject: [PATCH] nvmem: layouts: onie-tlv: drop driver owner initialization
+
+Core in nvmem_layout_driver_register() already sets the .owner, so
+driver does not need to.
+
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Reviewed-by: Michael Walle <mwalle@kernel.org>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240430084921.33387-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts/onie-tlv.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/nvmem/layouts/onie-tlv.c
++++ b/drivers/nvmem/layouts/onie-tlv.c
+@@ -247,7 +247,6 @@ MODULE_DEVICE_TABLE(of, onie_tlv_of_matc
+ static struct nvmem_layout_driver onie_tlv_layout = {
+       .driver = {
+-              .owner = THIS_MODULE,
+               .name = "onie-tlv-layout",
+               .of_match_table = onie_tlv_of_match_table,
+       },
diff --git a/target/linux/generic/backport-6.6/821-v6.10-0003-nvmem-layouts-sl28vpd-drop-driver-owner-initializati.patch b/target/linux/generic/backport-6.6/821-v6.10-0003-nvmem-layouts-sl28vpd-drop-driver-owner-initializati.patch
new file mode 100644 (file)
index 0000000..472a65f
--- /dev/null
@@ -0,0 +1,28 @@
+From 23fd602f21953c03c0714257d36685cd6b486f04 Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Date: Tue, 30 Apr 2024 09:49:13 +0100
+Subject: [PATCH] nvmem: layouts: sl28vpd: drop driver owner initialization
+
+Core in nvmem_layout_driver_register() already sets the .owner, so
+driver does not need to.
+
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Reviewed-by: Michael Walle <mwalle@kernel.org>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240430084921.33387-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts/sl28vpd.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/nvmem/layouts/sl28vpd.c
++++ b/drivers/nvmem/layouts/sl28vpd.c
+@@ -156,7 +156,6 @@ MODULE_DEVICE_TABLE(of, sl28vpd_of_match
+ static struct nvmem_layout_driver sl28vpd_layout = {
+       .driver = {
+-              .owner = THIS_MODULE,
+               .name = "kontron-sl28vpd-layout",
+               .of_match_table = sl28vpd_of_match_table,
+       },
diff --git a/target/linux/generic/backport-6.6/821-v6.10-0004-nvmem-sc27xx-fix-module-autoloading.patch b/target/linux/generic/backport-6.6/821-v6.10-0004-nvmem-sc27xx-fix-module-autoloading.patch
new file mode 100644 (file)
index 0000000..8f33f2a
--- /dev/null
@@ -0,0 +1,26 @@
+From dc3d88ade857ba3dca34f008e0b0aed3ef79cb15 Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzk@kernel.org>
+Date: Tue, 30 Apr 2024 09:49:14 +0100
+Subject: [PATCH] nvmem: sc27xx: fix module autoloading
+
+Add MODULE_DEVICE_TABLE(), so the module could be properly autoloaded
+based on the alias from of_device_id table.
+
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240430084921.33387-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sc27xx-efuse.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/nvmem/sc27xx-efuse.c
++++ b/drivers/nvmem/sc27xx-efuse.c
+@@ -262,6 +262,7 @@ static const struct of_device_id sc27xx_
+       { .compatible = "sprd,sc2730-efuse", .data = &sc2730_edata},
+       { }
+ };
++MODULE_DEVICE_TABLE(of, sc27xx_efuse_of_match);
+ static struct platform_driver sc27xx_efuse_driver = {
+       .probe = sc27xx_efuse_probe,
diff --git a/target/linux/generic/backport-6.6/821-v6.10-0005-nvmem-sprd-fix-module-autoloading.patch b/target/linux/generic/backport-6.6/821-v6.10-0005-nvmem-sprd-fix-module-autoloading.patch
new file mode 100644 (file)
index 0000000..0953c92
--- /dev/null
@@ -0,0 +1,26 @@
+From 154c1ec943e34f3188c9305b0c91d5e7dc1373b8 Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzk@kernel.org>
+Date: Tue, 30 Apr 2024 09:49:15 +0100
+Subject: [PATCH] nvmem: sprd: fix module autoloading
+
+Add MODULE_DEVICE_TABLE(), so the module could be properly autoloaded
+based on the alias from of_device_id table.
+
+Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240430084921.33387-6-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sprd-efuse.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/nvmem/sprd-efuse.c
++++ b/drivers/nvmem/sprd-efuse.c
+@@ -426,6 +426,7 @@ static const struct of_device_id sprd_ef
+       { .compatible = "sprd,ums312-efuse", .data = &ums312_data },
+       { }
+ };
++MODULE_DEVICE_TABLE(of, sprd_efuse_of_match);
+ static struct platform_driver sprd_efuse_driver = {
+       .probe = sprd_efuse_probe,
diff --git a/target/linux/generic/backport-6.6/821-v6.10-0006-nvmem-core-switch-to-use-device_add_groups.patch b/target/linux/generic/backport-6.6/821-v6.10-0006-nvmem-core-switch-to-use-device_add_groups.patch
new file mode 100644 (file)
index 0000000..14baafc
--- /dev/null
@@ -0,0 +1,32 @@
+From 8d8fc146dd7a0d6a6b37695747a524310dfb9d57 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Tue, 30 Apr 2024 09:49:16 +0100
+Subject: [PATCH] nvmem: core: switch to use device_add_groups()
+
+devm_device_add_groups() is being removed from the kernel, so move the
+nvmem driver to use device_add_groups() instead.  The logic is
+identical, when the device is removed the driver core will properly
+clean up and remove the groups, and the memory used by the attribute
+groups will be freed because it was created with dev_* calls, so this is
+functionally identical overall.
+
+Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Link: https://lore.kernel.org/r/20240430084921.33387-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -476,7 +476,7 @@ static int nvmem_populate_sysfs_cells(st
+       nvmem_cells_group.bin_attrs = cells_attrs;
+-      ret = devm_device_add_groups(&nvmem->dev, nvmem_cells_groups);
++      ret = device_add_groups(&nvmem->dev, nvmem_cells_groups);
+       if (ret)
+               goto unlock_mutex;
diff --git a/target/linux/generic/backport-6.6/821-v6.10-0007-nvmem-lpc18xx_eeprom-Convert-to-platform-remove-call.patch b/target/linux/generic/backport-6.6/821-v6.10-0007-nvmem-lpc18xx_eeprom-Convert-to-platform-remove-call.patch
new file mode 100644 (file)
index 0000000..27d9270
--- /dev/null
@@ -0,0 +1,57 @@
+From 693d2f629962628ddefc88f4b6b453edda5ac32e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
+Date: Tue, 30 Apr 2024 09:49:17 +0100
+Subject: [PATCH] nvmem: lpc18xx_eeprom: Convert to platform remove callback
+ returning void
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The .remove() callback for a platform driver returns an int which makes
+many driver authors wrongly assume it's possible to do error handling by
+returning an error code. However the value returned is ignored (apart
+from emitting a warning) and this typically results in resource leaks.
+
+To improve here there is a quest to make the remove callback return
+void. In the first step of this quest all drivers are converted to
+.remove_new(), which already returns void. Eventually after all drivers
+are converted, .remove_new() will be renamed to .remove().
+
+Trivially convert this driver from always returning zero in the remove
+callback to the void returning variant.
+
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Acked-by: Vladimir Zapolskiy <vz@mleia.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240430084921.33387-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/lpc18xx_eeprom.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/nvmem/lpc18xx_eeprom.c
++++ b/drivers/nvmem/lpc18xx_eeprom.c
+@@ -249,13 +249,11 @@ err_clk:
+       return ret;
+ }
+-static int lpc18xx_eeprom_remove(struct platform_device *pdev)
++static void lpc18xx_eeprom_remove(struct platform_device *pdev)
+ {
+       struct lpc18xx_eeprom_dev *eeprom = platform_get_drvdata(pdev);
+       clk_disable_unprepare(eeprom->clk);
+-
+-      return 0;
+ }
+ static const struct of_device_id lpc18xx_eeprom_of_match[] = {
+@@ -266,7 +264,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_eeprom_o
+ static struct platform_driver lpc18xx_eeprom_driver = {
+       .probe = lpc18xx_eeprom_probe,
+-      .remove = lpc18xx_eeprom_remove,
++      .remove_new = lpc18xx_eeprom_remove,
+       .driver = {
+               .name = "lpc18xx-eeprom",
+               .of_match_table = lpc18xx_eeprom_of_match,
diff --git a/target/linux/generic/backport-6.6/821-v6.10-0008-nvmem-meson-mx-efuse-Remove-nvmem_device-from-efuse-.patch b/target/linux/generic/backport-6.6/821-v6.10-0008-nvmem-meson-mx-efuse-Remove-nvmem_device-from-efuse-.patch
new file mode 100644 (file)
index 0000000..0b56ccc
--- /dev/null
@@ -0,0 +1,50 @@
+From 2a1ad6b75292d38aa2f6ded7335979e0632521da Mon Sep 17 00:00:00 2001
+From: Mukesh Ojha <quic_mojha@quicinc.com>
+Date: Tue, 30 Apr 2024 09:49:21 +0100
+Subject: [PATCH] nvmem: meson-mx-efuse: Remove nvmem_device from efuse struct
+
+nvmem_device is used at one place while registering nvmem
+device and it is not required to be present in efuse struct
+for just this purpose.
+
+Drop nvmem_device and manage with nvmem device stack variable.
+
+Signed-off-by: Mukesh Ojha <quic_mojha@quicinc.com>
+Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240430084921.33387-12-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/meson-mx-efuse.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/meson-mx-efuse.c
++++ b/drivers/nvmem/meson-mx-efuse.c
+@@ -43,7 +43,6 @@ struct meson_mx_efuse_platform_data {
+ struct meson_mx_efuse {
+       void __iomem *base;
+       struct clk *core_clk;
+-      struct nvmem_device *nvmem;
+       struct nvmem_config config;
+ };
+@@ -193,6 +192,7 @@ static int meson_mx_efuse_probe(struct p
+ {
+       const struct meson_mx_efuse_platform_data *drvdata;
+       struct meson_mx_efuse *efuse;
++      struct nvmem_device *nvmem;
+       drvdata = of_device_get_match_data(&pdev->dev);
+       if (!drvdata)
+@@ -223,9 +223,9 @@ static int meson_mx_efuse_probe(struct p
+               return PTR_ERR(efuse->core_clk);
+       }
+-      efuse->nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
++      nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);
+-      return PTR_ERR_OR_ZERO(efuse->nvmem);
++      return PTR_ERR_OR_ZERO(nvmem);
+ }
+ static struct platform_driver meson_mx_efuse_driver = {
diff --git a/target/linux/generic/backport-6.6/822-v6.11-0001-nvmem-add-missing-MODULE_DESCRIPTION-macros.patch b/target/linux/generic/backport-6.6/822-v6.11-0001-nvmem-add-missing-MODULE_DESCRIPTION-macros.patch
new file mode 100644 (file)
index 0000000..ec3fb14
--- /dev/null
@@ -0,0 +1,48 @@
+From c553bad4c5fc5ae44bd2fcaa73e1d6bedfb1c35c Mon Sep 17 00:00:00 2001
+From: Jeff Johnson <quic_jjohnson@quicinc.com>
+Date: Fri, 5 Jul 2024 08:48:38 +0100
+Subject: [PATCH] nvmem: add missing MODULE_DESCRIPTION() macros
+
+make allmodconfig && make W=1 C=1 reports:
+WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/nvmem/nvmem-apple-efuses.o
+WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/nvmem/nvmem_brcm_nvram.o
+WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/nvmem/nvmem_u-boot-env.o
+
+Add the missing invocations of the MODULE_DESCRIPTION() macro.
+
+Signed-off-by: Jeff Johnson <quic_jjohnson@quicinc.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240705074852.423202-2-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/apple-efuses.c | 1 +
+ drivers/nvmem/brcm_nvram.c   | 1 +
+ drivers/nvmem/u-boot-env.c   | 1 +
+ 3 files changed, 3 insertions(+)
+
+--- a/drivers/nvmem/apple-efuses.c
++++ b/drivers/nvmem/apple-efuses.c
+@@ -78,4 +78,5 @@ static struct platform_driver apple_efus
+ module_platform_driver(apple_efuses_driver);
+ MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
++MODULE_DESCRIPTION("Apple SoC eFuse driver");
+ MODULE_LICENSE("GPL");
+--- a/drivers/nvmem/brcm_nvram.c
++++ b/drivers/nvmem/brcm_nvram.c
+@@ -253,5 +253,6 @@ static int __init brcm_nvram_init(void)
+ subsys_initcall_sync(brcm_nvram_init);
+ MODULE_AUTHOR("Rafał Miłecki");
++MODULE_DESCRIPTION("Broadcom I/O-mapped NVRAM support driver");
+ MODULE_LICENSE("GPL");
+ MODULE_DEVICE_TABLE(of, brcm_nvram_of_match_table);
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -256,5 +256,6 @@ static struct platform_driver u_boot_env
+ module_platform_driver(u_boot_env_driver);
+ MODULE_AUTHOR("Rafał Miłecki");
++MODULE_DESCRIPTION("U-Boot environment variables support module");
+ MODULE_LICENSE("GPL");
+ MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table);
diff --git a/target/linux/generic/backport-6.6/822-v6.11-0002-nvmem-meson-efuse-Replacing-the-use-of-of_node_put-t.patch b/target/linux/generic/backport-6.6/822-v6.11-0002-nvmem-meson-efuse-Replacing-the-use-of-of_node_put-t.patch
new file mode 100644 (file)
index 0000000..05c82db
--- /dev/null
@@ -0,0 +1,48 @@
+From 5fecb932607d83d37a703c731268e9d9051457f5 Mon Sep 17 00:00:00 2001
+From: MarileneGarcia <marilene.agarcia@gmail.com>
+Date: Fri, 5 Jul 2024 08:48:40 +0100
+Subject: [PATCH] nvmem: meson-efuse: Replacing the use of of_node_put to
+ __free
+
+Use __free for device_node values, and thus drop calls to
+of_node_put.
+
+The goal is to reduce memory management issues by using this
+scope-based of_node_put() cleanup to simplify function exit
+handling. When using __free a resource is allocated within a
+block, it is automatically freed at the end of the block.
+
+Suggested-by: Julia Lawall <julia.lawall@inria.fr>
+Signed-off-by: MarileneGarcia <marilene.agarcia@gmail.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240705074852.423202-4-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/meson-efuse.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/meson-efuse.c
++++ b/drivers/nvmem/meson-efuse.c
+@@ -48,20 +48,19 @@ static int meson_efuse_probe(struct plat
+ {
+       struct device *dev = &pdev->dev;
+       struct meson_sm_firmware *fw;
+-      struct device_node *sm_np;
+       struct nvmem_device *nvmem;
+       struct nvmem_config *econfig;
+       struct clk *clk;
+       unsigned int size;
++      struct device_node *sm_np __free(device_node) =
++                      of_parse_phandle(pdev->dev.of_node, "secure-monitor", 0);
+-      sm_np = of_parse_phandle(pdev->dev.of_node, "secure-monitor", 0);
+       if (!sm_np) {
+               dev_err(&pdev->dev, "no secure-monitor node\n");
+               return -ENODEV;
+       }
+       fw = meson_sm_get(sm_np);
+-      of_node_put(sm_np);
+       if (!fw)
+               return -EPROBE_DEFER;
diff --git a/target/linux/generic/backport-6.6/822-v6.11-0004-nvmem-rockchip-otp-Set-type-to-OTP.patch b/target/linux/generic/backport-6.6/822-v6.11-0004-nvmem-rockchip-otp-Set-type-to-OTP.patch
new file mode 100644 (file)
index 0000000..8491eb3
--- /dev/null
@@ -0,0 +1,25 @@
+From 39f95600d8c53355b212a117e91a6ba15e0cac47 Mon Sep 17 00:00:00 2001
+From: Heiko Stuebner <heiko.stuebner@cherry.de>
+Date: Fri, 5 Jul 2024 08:48:42 +0100
+Subject: [PATCH] nvmem: rockchip-otp: Set type to OTP
+
+The Rockchip OTP is obviously an OTP memory, so document this fact.
+
+Signed-off-by: Heiko Stuebner <heiko.stuebner@cherry.de>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240705074852.423202-6-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/rockchip-otp.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/nvmem/rockchip-otp.c
++++ b/drivers/nvmem/rockchip-otp.c
+@@ -256,6 +256,7 @@ static struct nvmem_config otp_config =
+       .name = "rockchip-otp",
+       .owner = THIS_MODULE,
+       .add_legacy_fixed_of_cells = true,
++      .type = NVMEM_TYPE_OTP,
+       .read_only = true,
+       .stride = 1,
+       .word_size = 1,
diff --git a/target/linux/generic/backport-6.6/822-v6.11-0005-nvmem-rockchip-efuse-set-type-to-OTP.patch b/target/linux/generic/backport-6.6/822-v6.11-0005-nvmem-rockchip-efuse-set-type-to-OTP.patch
new file mode 100644 (file)
index 0000000..260b03e
--- /dev/null
@@ -0,0 +1,26 @@
+From ba64a04474d2989f397982c48e405cfd785e2dd5 Mon Sep 17 00:00:00 2001
+From: Heiko Stuebner <heiko.stuebner@cherry.de>
+Date: Fri, 5 Jul 2024 08:48:43 +0100
+Subject: [PATCH] nvmem: rockchip-efuse: set type to OTP
+
+This device currently reports an "Unknown" type in sysfs.
+Since it is an eFuse hardware device, set its type to OTP.
+
+Signed-off-by: Heiko Stuebner <heiko.stuebner@cherry.de>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240705074852.423202-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/rockchip-efuse.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/nvmem/rockchip-efuse.c
++++ b/drivers/nvmem/rockchip-efuse.c
+@@ -206,6 +206,7 @@ static int rockchip_rk3399_efuse_read(vo
+ static struct nvmem_config econfig = {
+       .name = "rockchip-efuse",
+       .add_legacy_fixed_of_cells = true,
++      .type = NVMEM_TYPE_OTP,
+       .stride = 1,
+       .word_size = 1,
+       .read_only = true,
diff --git a/target/linux/generic/backport-6.6/822-v6.11-0006-nvmem-core-add-single-sysfs-group.patch b/target/linux/generic/backport-6.6/822-v6.11-0006-nvmem-core-add-single-sysfs-group.patch
new file mode 100644 (file)
index 0000000..eae06b2
--- /dev/null
@@ -0,0 +1,42 @@
+From 6188f233161c6a5b2d1c396a221dfafc77dc9eec Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <linux@weissschuh.net>
+Date: Fri, 5 Jul 2024 08:48:46 +0100
+Subject: [PATCH] nvmem: core: add single sysfs group
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The sysfs core provides a function to easily register a single group.
+Use it and remove the now unnecessary nvmem_cells_groups array.
+
+Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240705074852.423202-10-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -367,11 +367,6 @@ static const struct attribute_group *nvm
+       NULL,
+ };
+-static const struct attribute_group *nvmem_cells_groups[] = {
+-      &nvmem_cells_group,
+-      NULL,
+-};
+-
+ static struct bin_attribute bin_attr_nvmem_eeprom_compat = {
+       .attr   = {
+               .name   = "eeprom",
+@@ -476,7 +471,7 @@ static int nvmem_populate_sysfs_cells(st
+       nvmem_cells_group.bin_attrs = cells_attrs;
+-      ret = device_add_groups(&nvmem->dev, nvmem_cells_groups);
++      ret = device_add_group(&nvmem->dev, &nvmem_cells_group);
+       if (ret)
+               goto unlock_mutex;
diff --git a/target/linux/generic/backport-6.6/822-v6.11-0007-nvmem-core-remove-global-nvmem_cells_group.patch b/target/linux/generic/backport-6.6/822-v6.11-0007-nvmem-core-remove-global-nvmem_cells_group.patch
new file mode 100644 (file)
index 0000000..9ec523c
--- /dev/null
@@ -0,0 +1,83 @@
+From 6839fed062b7898665983368c88269a6fb1fc10f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <linux@weissschuh.net>
+Date: Fri, 5 Jul 2024 08:48:47 +0100
+Subject: [PATCH] nvmem: core: remove global nvmem_cells_group
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+nvmem_cells_groups is a global variable that is also mutated.
+This is complicated and error-prone.
+
+Instead use a normal stack variable.
+
+Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240705074852.423202-11-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 26 ++++++++++----------------
+ 1 file changed, 10 insertions(+), 16 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -357,11 +357,6 @@ static const struct attribute_group nvme
+       .is_bin_visible = nvmem_bin_attr_is_visible,
+ };
+-/* Cell attributes will be dynamically allocated */
+-static struct attribute_group nvmem_cells_group = {
+-      .name           = "cells",
+-};
+-
+ static const struct attribute_group *nvmem_dev_groups[] = {
+       &nvmem_bin_group,
+       NULL,
+@@ -423,23 +418,24 @@ static void nvmem_sysfs_remove_compat(st
+ static int nvmem_populate_sysfs_cells(struct nvmem_device *nvmem)
+ {
+-      struct bin_attribute **cells_attrs, *attrs;
++      struct attribute_group group = {
++              .name   = "cells",
++      };
+       struct nvmem_cell_entry *entry;
++      struct bin_attribute *attrs;
+       unsigned int ncells = 0, i = 0;
+       int ret = 0;
+       mutex_lock(&nvmem_mutex);
+-      if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated) {
+-              nvmem_cells_group.bin_attrs = NULL;
++      if (list_empty(&nvmem->cells) || nvmem->sysfs_cells_populated)
+               goto unlock_mutex;
+-      }
+       /* Allocate an array of attributes with a sentinel */
+       ncells = list_count_nodes(&nvmem->cells);
+-      cells_attrs = devm_kcalloc(&nvmem->dev, ncells + 1,
+-                                 sizeof(struct bin_attribute *), GFP_KERNEL);
+-      if (!cells_attrs) {
++      group.bin_attrs = devm_kcalloc(&nvmem->dev, ncells + 1,
++                                     sizeof(struct bin_attribute *), GFP_KERNEL);
++      if (!group.bin_attrs) {
+               ret = -ENOMEM;
+               goto unlock_mutex;
+       }
+@@ -465,13 +461,11 @@ static int nvmem_populate_sysfs_cells(st
+                       goto unlock_mutex;
+               }
+-              cells_attrs[i] = &attrs[i];
++              group.bin_attrs[i] = &attrs[i];
+               i++;
+       }
+-      nvmem_cells_group.bin_attrs = cells_attrs;
+-
+-      ret = device_add_group(&nvmem->dev, &nvmem_cells_group);
++      ret = device_add_group(&nvmem->dev, &group);
+       if (ret)
+               goto unlock_mutex;
diff --git a/target/linux/generic/backport-6.6/822-v6.11-0008-nvmem-core-drop-unnecessary-range-checks-in-sysfs-ca.patch b/target/linux/generic/backport-6.6/822-v6.11-0008-nvmem-core-drop-unnecessary-range-checks-in-sysfs-ca.patch
new file mode 100644 (file)
index 0000000..22408e5
--- /dev/null
@@ -0,0 +1,61 @@
+From 588773802c386d38f9c4e91acd47369e89d95a30 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <linux@weissschuh.net>
+Date: Fri, 5 Jul 2024 08:48:48 +0100
+Subject: [PATCH] nvmem: core: drop unnecessary range checks in sysfs callbacks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The same checks have already been done in sysfs_kf_bin_write() and
+sysfs_kf_bin_read() just before the callbacks are invoked.
+
+Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240705074852.423202-12-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 14 --------------
+ 1 file changed, 14 deletions(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -203,19 +203,12 @@ static ssize_t bin_attr_nvmem_read(struc
+               dev = kobj_to_dev(kobj);
+       nvmem = to_nvmem_device(dev);
+-      /* Stop the user from reading */
+-      if (pos >= nvmem->size)
+-              return 0;
+-
+       if (!IS_ALIGNED(pos, nvmem->stride))
+               return -EINVAL;
+       if (count < nvmem->word_size)
+               return -EINVAL;
+-      if (pos + count > nvmem->size)
+-              count = nvmem->size - pos;
+-
+       count = round_down(count, nvmem->word_size);
+       if (!nvmem->reg_read)
+@@ -243,19 +236,12 @@ static ssize_t bin_attr_nvmem_write(stru
+               dev = kobj_to_dev(kobj);
+       nvmem = to_nvmem_device(dev);
+-      /* Stop the user from writing */
+-      if (pos >= nvmem->size)
+-              return -EFBIG;
+-
+       if (!IS_ALIGNED(pos, nvmem->stride))
+               return -EINVAL;
+       if (count < nvmem->word_size)
+               return -EINVAL;
+-      if (pos + count > nvmem->size)
+-              count = nvmem->size - pos;
+-
+       count = round_down(count, nvmem->word_size);
+       if (!nvmem->reg_write)
diff --git a/target/linux/generic/backport-6.6/822-v6.11-0009-nvmem-Use-sysfs_emit-for-type-attribute.patch b/target/linux/generic/backport-6.6/822-v6.11-0009-nvmem-Use-sysfs_emit-for-type-attribute.patch
new file mode 100644 (file)
index 0000000..3159efb
--- /dev/null
@@ -0,0 +1,30 @@
+From 08c367e45b6d322956878774f0b88bf5e52c6d54 Mon Sep 17 00:00:00 2001
+From: Marek Vasut <marex@denx.de>
+Date: Fri, 5 Jul 2024 08:48:51 +0100
+Subject: [PATCH] nvmem: Use sysfs_emit() for type attribute
+
+Use sysfs_emit() instead of sprintf() to follow best practice per
+Documentation/filesystems/sysfs.rst
+"
+show() should only use sysfs_emit()...
+"
+
+Signed-off-by: Marek Vasut <marex@denx.de>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240705074852.423202-15-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -179,7 +179,7 @@ static ssize_t type_show(struct device *
+ {
+       struct nvmem_device *nvmem = to_nvmem_device(dev);
+-      return sprintf(buf, "%s\n", nvmem_type_str[nvmem->type]);
++      return sysfs_emit(buf, "%s\n", nvmem_type_str[nvmem->type]);
+ }
+ static DEVICE_ATTR_RO(type);
diff --git a/target/linux/generic/backport-6.6/822-v6.11-0010-nvmem-core-Implement-force_ro-sysfs-attribute.patch b/target/linux/generic/backport-6.6/822-v6.11-0010-nvmem-core-Implement-force_ro-sysfs-attribute.patch
new file mode 100644 (file)
index 0000000..28fb544
--- /dev/null
@@ -0,0 +1,122 @@
+From 9d7eb234ac7a56b88aea8a52ed81553a730fe25c Mon Sep 17 00:00:00 2001
+From: Marek Vasut <marex@denx.de>
+Date: Fri, 5 Jul 2024 08:48:52 +0100
+Subject: [PATCH] nvmem: core: Implement force_ro sysfs attribute
+
+Implement "force_ro" sysfs attribute to allow users to set read-write
+devices as read-only and back to read-write from userspace. The choice
+of the name is based on MMC core 'force_ro' attribute.
+
+This solves a situation where an AT24 I2C EEPROM with GPIO based nWP
+signal may have to be occasionally updated. Such I2C EEPROM device is
+usually set as read-only during most of the regular system operation,
+but in case it has to be updated in a controlled manner, it could be
+unlocked using this new "force_ro" sysfs attribute and then re-locked
+again.
+
+The "read-only" DT property and config->read_only configuration is
+respected and is used to set default state of the device, read-only
+or read-write, for devices which do implement .reg_write function.
+For devices which do not implement .reg_write function, the device
+is unconditionally read-only and the "force_ro" attribute is not
+visible.
+
+Signed-off-by: Marek Vasut <marex@denx.de>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240705074852.423202-16-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/ABI/stable/sysfs-bus-nvmem | 17 ++++++++++
+ drivers/nvmem/core.c                     | 43 ++++++++++++++++++++++++
+ 2 files changed, 60 insertions(+)
+
+--- a/Documentation/ABI/stable/sysfs-bus-nvmem
++++ b/Documentation/ABI/stable/sysfs-bus-nvmem
+@@ -1,3 +1,20 @@
++What:         /sys/bus/nvmem/devices/.../force_ro
++Date:         June 2024
++KernelVersion:        6.11
++Contact:      Marek Vasut <marex@denx.de>
++Description:
++              This read/write attribute allows users to set read-write
++              devices as read-only and back to read-write from userspace.
++              This can be used to unlock and relock write-protection of
++              devices which are generally locked, except during sporadic
++              programming operation.
++              Read returns '0' or '1' for read-write or read-only modes
++              respectively.
++              Write parses one of 'YyTt1NnFf0', or [oO][NnFf] for "on"
++              and "off", i.e. what kstrbool() supports.
++              Note: This file is only present if CONFIG_NVMEM_SYSFS
++              is enabled.
++
+ What:         /sys/bus/nvmem/devices/.../nvmem
+ Date:         July 2015
+ KernelVersion:  4.2
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -184,7 +184,30 @@ static ssize_t type_show(struct device *
+ static DEVICE_ATTR_RO(type);
++static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,
++                           char *buf)
++{
++      struct nvmem_device *nvmem = to_nvmem_device(dev);
++
++      return sysfs_emit(buf, "%d\n", nvmem->read_only);
++}
++
++static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr,
++                            const char *buf, size_t count)
++{
++      struct nvmem_device *nvmem = to_nvmem_device(dev);
++      int ret = kstrtobool(buf, &nvmem->read_only);
++
++      if (ret < 0)
++              return ret;
++
++      return count;
++}
++
++static DEVICE_ATTR_RW(force_ro);
++
+ static struct attribute *nvmem_attrs[] = {
++      &dev_attr_force_ro.attr,
+       &dev_attr_type.attr,
+       NULL,
+ };
+@@ -285,6 +308,25 @@ static umode_t nvmem_bin_attr_is_visible
+       return nvmem_bin_attr_get_umode(nvmem);
+ }
++static umode_t nvmem_attr_is_visible(struct kobject *kobj,
++                                   struct attribute *attr, int i)
++{
++      struct device *dev = kobj_to_dev(kobj);
++      struct nvmem_device *nvmem = to_nvmem_device(dev);
++
++      /*
++       * If the device has no .reg_write operation, do not allow
++       * configuration as read-write.
++       * If the device is set as read-only by configuration, it
++       * can be forced into read-write mode using the 'force_ro'
++       * attribute.
++       */
++      if (attr == &dev_attr_force_ro.attr && !nvmem->reg_write)
++              return 0;       /* Attribute not visible */
++
++      return attr->mode;
++}
++
+ static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
+                                           const char *id, int index);
+@@ -341,6 +383,7 @@ static const struct attribute_group nvme
+       .bin_attrs      = nvmem_bin_attributes,
+       .attrs          = nvmem_attrs,
+       .is_bin_visible = nvmem_bin_attr_is_visible,
++      .is_visible     = nvmem_attr_is_visible,
+ };
+ static const struct attribute_group *nvmem_dev_groups[] = {
diff --git a/target/linux/generic/backport-6.6/823-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch b/target/linux/generic/backport-6.6/823-v6.12-0001-nvmem-imx-ocotp-ele-support-i.MX95.patch
new file mode 100644 (file)
index 0000000..c19931b
--- /dev/null
@@ -0,0 +1,73 @@
+From c3f9b7b4e5f9de319d00784577cda42036ff243a Mon Sep 17 00:00:00 2001
+From: Peng Fan <peng.fan@nxp.com>
+Date: Mon, 2 Sep 2024 15:29:45 +0100
+Subject: [PATCH] nvmem: imx-ocotp-ele: support i.MX95
+
+i.MX95 OCOTP has same accessing method, so add an entry for i.MX95, but
+some fuse has ECC feature, so only read out the lower 16bits for ECC fuses.
+
+Signed-off-by: Peng Fan <peng.fan@nxp.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/imx-ocotp-ele.c | 32 +++++++++++++++++++++++++++++---
+ 1 file changed, 29 insertions(+), 3 deletions(-)
+
+--- a/drivers/nvmem/imx-ocotp-ele.c
++++ b/drivers/nvmem/imx-ocotp-ele.c
+@@ -14,8 +14,9 @@
+ #include <linux/slab.h>
+ enum fuse_type {
+-      FUSE_FSB = 1,
+-      FUSE_ELE = 2,
++      FUSE_FSB = BIT(0),
++      FUSE_ELE = BIT(1),
++      FUSE_ECC = BIT(2),
+       FUSE_INVALID = -1
+ };
+@@ -93,7 +94,10 @@ static int imx_ocotp_reg_read(void *cont
+                       continue;
+               }
+-              *buf++ = readl_relaxed(reg + (i << 2));
++              if (type & FUSE_ECC)
++                      *buf++ = readl_relaxed(reg + (i << 2)) & GENMASK(15, 0);
++              else
++                      *buf++ = readl_relaxed(reg + (i << 2));
+       }
+       memcpy(val, (u8 *)p, bytes);
+@@ -155,8 +159,30 @@ static const struct ocotp_devtype_data i
+       },
+ };
++static const struct ocotp_devtype_data imx95_ocotp_data = {
++      .reg_off = 0x8000,
++      .reg_read = imx_ocotp_reg_read,
++      .size = 2048,
++      .num_entry = 12,
++      .entry = {
++              { 0, 1, FUSE_FSB | FUSE_ECC },
++              { 7, 1, FUSE_FSB | FUSE_ECC },
++              { 9, 3, FUSE_FSB | FUSE_ECC },
++              { 12, 24, FUSE_FSB },
++              { 36, 2, FUSE_FSB  | FUSE_ECC },
++              { 38, 14, FUSE_FSB },
++              { 63, 1, FUSE_ELE },
++              { 128, 16, FUSE_ELE },
++              { 188, 1, FUSE_ELE },
++              { 317, 2, FUSE_FSB | FUSE_ECC },
++              { 320, 7, FUSE_FSB },
++              { 328, 184, FUSE_FSB }
++      },
++};
++
+ static const struct of_device_id imx_ele_ocotp_dt_ids[] = {
+       { .compatible = "fsl,imx93-ocotp", .data = &imx93_ocotp_data, },
++      { .compatible = "fsl,imx95-ocotp", .data = &imx95_ocotp_data, },
+       {},
+ };
+ MODULE_DEVICE_TABLE(of, imx_ele_ocotp_dt_ids);
diff --git a/target/linux/generic/backport-6.6/823-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch b/target/linux/generic/backport-6.6/823-v6.12-0002-nvmem-sunplus-ocotp-Use-devm_platform_ioremap_resour.patch
new file mode 100644 (file)
index 0000000..13ef50b
--- /dev/null
@@ -0,0 +1,44 @@
+From 98ee46391baf35987227236d0c3bb30ab6e758c8 Mon Sep 17 00:00:00 2001
+From: Zhang Zekun <zhangzekun11@huawei.com>
+Date: Mon, 2 Sep 2024 15:29:50 +0100
+Subject: [PATCH] nvmem: sunplus-ocotp: Use
+ devm_platform_ioremap_resource_byname() helper function
+
+platform_get_resource_byname() and devm_ioremap_resource() can be
+replaced by devm_platform_ioremap_resource_byname(), which can
+simplify the code logic a bit, No functional change here.
+
+Signed-off-by: Zhang Zekun <zhangzekun11@huawei.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/sunplus-ocotp.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/drivers/nvmem/sunplus-ocotp.c
++++ b/drivers/nvmem/sunplus-ocotp.c
+@@ -159,7 +159,6 @@ static int sp_ocotp_probe(struct platfor
+       struct device *dev = &pdev->dev;
+       struct nvmem_device *nvmem;
+       struct sp_ocotp_priv *otp;
+-      struct resource *res;
+       int ret;
+       otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
+@@ -168,13 +167,11 @@ static int sp_ocotp_probe(struct platfor
+       otp->dev = dev;
+-      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hb_gpio");
+-      otp->base[HB_GPIO] = devm_ioremap_resource(dev, res);
++      otp->base[HB_GPIO] = devm_platform_ioremap_resource_byname(pdev, "hb_gpio");
+       if (IS_ERR(otp->base[HB_GPIO]))
+               return PTR_ERR(otp->base[HB_GPIO]);
+-      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otprx");
+-      otp->base[OTPRX] = devm_ioremap_resource(dev, res);
++      otp->base[OTPRX] = devm_platform_ioremap_resource_byname(pdev, "otprx");
+       if (IS_ERR(otp->base[OTPRX]))
+               return PTR_ERR(otp->base[OTPRX]);
diff --git a/target/linux/generic/backport-6.6/823-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch b/target/linux/generic/backport-6.6/823-v6.12-0003-nvmem-layouts-add-U-Boot-env-layout.patch
new file mode 100644 (file)
index 0000000..15a0f77
--- /dev/null
@@ -0,0 +1,525 @@
+From 5f15811286aff4664bf275a7ede64e1b8858151b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 2 Sep 2024 15:29:47 +0100
+Subject: [PATCH] nvmem: layouts: add U-Boot env layout
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+U-Boot environment variables are stored in a specific format. Actual
+data can be placed in various storage sources (MTD, UBI volume, EEPROM,
+NVRAM, etc.).
+
+Move all generic (NVMEM device independent) code from NVMEM device
+driver to an NVMEM layout driver. Then add a simple NVMEM layout code on
+top of it.
+
+This allows using NVMEM layout for parsing U-Boot env data stored in any
+kind of NVMEM device.
+
+The old NVMEM glue driver stays in place for handling bindings in the
+MTD context. To avoid code duplication it uses exported layout parsing
+function. Please note that handling MTD & NVMEM layout bindings may be
+refactored in the future.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240902142952.71639-5-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ MAINTAINERS                        |   1 +
+ drivers/nvmem/Kconfig              |   3 +-
+ drivers/nvmem/layouts/Kconfig      |  11 ++
+ drivers/nvmem/layouts/Makefile     |   1 +
+ drivers/nvmem/layouts/u-boot-env.c | 211 +++++++++++++++++++++++++++++
+ drivers/nvmem/layouts/u-boot-env.h |  15 ++
+ drivers/nvmem/u-boot-env.c         | 165 +---------------------
+ 7 files changed, 242 insertions(+), 165 deletions(-)
+ create mode 100644 drivers/nvmem/layouts/u-boot-env.c
+ create mode 100644 drivers/nvmem/layouts/u-boot-env.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -21988,6 +21988,7 @@ U-BOOT ENVIRONMENT VARIABLES
+ M:    Rafał Miłecki <rafal@milecki.pl>
+ S:    Maintained
+ F:    Documentation/devicetree/bindings/nvmem/u-boot,env.yaml
++F:    drivers/nvmem/layouts/u-boot-env.c
+ F:    drivers/nvmem/u-boot-env.c
+ UACCE ACCELERATOR FRAMEWORK
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -363,8 +363,7 @@ config NVMEM_SUNXI_SID
+ config NVMEM_U_BOOT_ENV
+       tristate "U-Boot environment variables support"
+       depends on OF && MTD
+-      select CRC32
+-      select GENERIC_NET_UTILS
++      select NVMEM_LAYOUT_U_BOOT_ENV
+       help
+         U-Boot stores its setup as environment variables. This driver adds
+         support for verifying & exporting such data. It also exposes variables
+--- a/drivers/nvmem/layouts/Kconfig
++++ b/drivers/nvmem/layouts/Kconfig
+@@ -26,6 +26,17 @@ config NVMEM_LAYOUT_ONIE_TLV
+         If unsure, say N.
++config NVMEM_LAYOUT_U_BOOT_ENV
++      tristate "U-Boot environment variables layout"
++      select CRC32
++      select GENERIC_NET_UTILS
++      help
++        U-Boot stores its setup as environment variables. This driver adds
++        support for verifying & exporting such data. It also exposes variables
++        as NVMEM cells so they can be referenced by other drivers.
++
++        If unsure, say N.
++
+ endmenu
+ endif
+--- a/drivers/nvmem/layouts/Makefile
++++ b/drivers/nvmem/layouts/Makefile
+@@ -5,3 +5,4 @@
+ obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
+ obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
++obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
+--- /dev/null
++++ b/drivers/nvmem/layouts/u-boot-env.c
+@@ -0,0 +1,211 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2022 - 2023 Rafał Miłecki <rafal@milecki.pl>
++ */
++
++#include <linux/crc32.h>
++#include <linux/etherdevice.h>
++#include <linux/export.h>
++#include <linux/if_ether.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/slab.h>
++
++#include "u-boot-env.h"
++
++struct u_boot_env_image_single {
++      __le32 crc32;
++      uint8_t data[];
++} __packed;
++
++struct u_boot_env_image_redundant {
++      __le32 crc32;
++      u8 mark;
++      uint8_t data[];
++} __packed;
++
++struct u_boot_env_image_broadcom {
++      __le32 magic;
++      __le32 len;
++      __le32 crc32;
++      DECLARE_FLEX_ARRAY(uint8_t, data);
++} __packed;
++
++static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
++                                              unsigned int offset, void *buf, size_t bytes)
++{
++      u8 mac[ETH_ALEN];
++
++      if (bytes != 3 * ETH_ALEN - 1)
++              return -EINVAL;
++
++      if (!mac_pton(buf, mac))
++              return -EINVAL;
++
++      if (index)
++              eth_addr_add(mac, index);
++
++      ether_addr_copy(buf, mac);
++
++      return 0;
++}
++
++static int u_boot_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf,
++                                size_t data_offset, size_t data_len)
++{
++      char *data = buf + data_offset;
++      char *var, *value, *eq;
++
++      for (var = data;
++           var < data + data_len && *var;
++           var = value + strlen(value) + 1) {
++              struct nvmem_cell_info info = {};
++
++              eq = strchr(var, '=');
++              if (!eq)
++                      break;
++              *eq = '\0';
++              value = eq + 1;
++
++              info.name = devm_kstrdup(dev, var, GFP_KERNEL);
++              if (!info.name)
++                      return -ENOMEM;
++              info.offset = data_offset + value - data;
++              info.bytes = strlen(value);
++              info.np = of_get_child_by_name(dev->of_node, info.name);
++              if (!strcmp(var, "ethaddr")) {
++                      info.raw_len = strlen(value);
++                      info.bytes = ETH_ALEN;
++                      info.read_post_process = u_boot_env_read_post_process_ethaddr;
++              }
++
++              nvmem_add_one_cell(nvmem, &info);
++      }
++
++      return 0;
++}
++
++int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
++                   enum u_boot_env_format format)
++{
++      size_t crc32_data_offset;
++      size_t crc32_data_len;
++      size_t crc32_offset;
++      __le32 *crc32_addr;
++      size_t data_offset;
++      size_t data_len;
++      size_t dev_size;
++      uint32_t crc32;
++      uint32_t calc;
++      uint8_t *buf;
++      int bytes;
++      int err;
++
++      dev_size = nvmem_dev_size(nvmem);
++
++      buf = kzalloc(dev_size, GFP_KERNEL);
++      if (!buf) {
++              err = -ENOMEM;
++              goto err_out;
++      }
++
++      bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
++      if (bytes < 0) {
++              err = bytes;
++              goto err_kfree;
++      } else if (bytes != dev_size) {
++              err = -EIO;
++              goto err_kfree;
++      }
++
++      switch (format) {
++      case U_BOOT_FORMAT_SINGLE:
++              crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
++              crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
++              data_offset = offsetof(struct u_boot_env_image_single, data);
++              break;
++      case U_BOOT_FORMAT_REDUNDANT:
++              crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
++              crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
++              data_offset = offsetof(struct u_boot_env_image_redundant, data);
++              break;
++      case U_BOOT_FORMAT_BROADCOM:
++              crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
++              crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++              data_offset = offsetof(struct u_boot_env_image_broadcom, data);
++              break;
++      }
++
++      if (dev_size < data_offset) {
++              dev_err(dev, "Device too small for u-boot-env\n");
++              err = -EIO;
++              goto err_kfree;
++      }
++
++      crc32_addr = (__le32 *)(buf + crc32_offset);
++      crc32 = le32_to_cpu(*crc32_addr);
++      crc32_data_len = dev_size - crc32_data_offset;
++      data_len = dev_size - data_offset;
++
++      calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
++      if (calc != crc32) {
++              dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
++              err = -EINVAL;
++              goto err_kfree;
++      }
++
++      buf[dev_size - 1] = '\0';
++      err = u_boot_env_parse_cells(dev, nvmem, buf, data_offset, data_len);
++
++err_kfree:
++      kfree(buf);
++err_out:
++      return err;
++}
++EXPORT_SYMBOL_GPL(u_boot_env_parse);
++
++static int u_boot_env_add_cells(struct nvmem_layout *layout)
++{
++      struct device *dev = &layout->dev;
++      enum u_boot_env_format format;
++
++      format = (uintptr_t)device_get_match_data(dev);
++
++      return u_boot_env_parse(dev, layout->nvmem, format);
++}
++
++static int u_boot_env_probe(struct nvmem_layout *layout)
++{
++      layout->add_cells = u_boot_env_add_cells;
++
++      return nvmem_layout_register(layout);
++}
++
++static void u_boot_env_remove(struct nvmem_layout *layout)
++{
++      nvmem_layout_unregister(layout);
++}
++
++static const struct of_device_id u_boot_env_of_match_table[] = {
++      { .compatible = "u-boot,env", .data = (void *)U_BOOT_FORMAT_SINGLE, },
++      { .compatible = "u-boot,env-redundant-bool", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++      { .compatible = "u-boot,env-redundant-count", .data = (void *)U_BOOT_FORMAT_REDUNDANT, },
++      { .compatible = "brcm,env", .data = (void *)U_BOOT_FORMAT_BROADCOM, },
++      {},
++};
++
++static struct nvmem_layout_driver u_boot_env_layout = {
++      .driver = {
++              .name = "u-boot-env-layout",
++              .of_match_table = u_boot_env_of_match_table,
++      },
++      .probe = u_boot_env_probe,
++      .remove = u_boot_env_remove,
++};
++module_nvmem_layout_driver(u_boot_env_layout);
++
++MODULE_AUTHOR("Rafał Miłecki");
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(of, u_boot_env_of_match_table);
++MODULE_DESCRIPTION("NVMEM layout driver for U-Boot environment variables");
+--- /dev/null
++++ b/drivers/nvmem/layouts/u-boot-env.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
++#define _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H
++
++enum u_boot_env_format {
++      U_BOOT_FORMAT_SINGLE,
++      U_BOOT_FORMAT_REDUNDANT,
++      U_BOOT_FORMAT_BROADCOM,
++};
++
++int u_boot_env_parse(struct device *dev, struct nvmem_device *nvmem,
++                   enum u_boot_env_format format);
++
++#endif  /* ifndef _LINUX_NVMEM_LAYOUTS_U_BOOT_ENV_H */
+--- a/drivers/nvmem/u-boot-env.c
++++ b/drivers/nvmem/u-boot-env.c
+@@ -3,23 +3,15 @@
+  * Copyright (C) 2022 Rafał Miłecki <rafal@milecki.pl>
+  */
+-#include <linux/crc32.h>
+-#include <linux/etherdevice.h>
+-#include <linux/if_ether.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/nvmem-consumer.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+-enum u_boot_env_format {
+-      U_BOOT_FORMAT_SINGLE,
+-      U_BOOT_FORMAT_REDUNDANT,
+-      U_BOOT_FORMAT_BROADCOM,
+-};
++#include "layouts/u-boot-env.h"
+ struct u_boot_env {
+       struct device *dev;
+@@ -29,24 +21,6 @@ struct u_boot_env {
+       struct mtd_info *mtd;
+ };
+-struct u_boot_env_image_single {
+-      __le32 crc32;
+-      uint8_t data[];
+-} __packed;
+-
+-struct u_boot_env_image_redundant {
+-      __le32 crc32;
+-      u8 mark;
+-      uint8_t data[];
+-} __packed;
+-
+-struct u_boot_env_image_broadcom {
+-      __le32 magic;
+-      __le32 len;
+-      __le32 crc32;
+-      DECLARE_FLEX_ARRAY(uint8_t, data);
+-} __packed;
+-
+ static int u_boot_env_read(void *context, unsigned int offset, void *val,
+                          size_t bytes)
+ {
+@@ -69,141 +43,6 @@ static int u_boot_env_read(void *context
+       return 0;
+ }
+-static int u_boot_env_read_post_process_ethaddr(void *context, const char *id, int index,
+-                                              unsigned int offset, void *buf, size_t bytes)
+-{
+-      u8 mac[ETH_ALEN];
+-
+-      if (bytes != 3 * ETH_ALEN - 1)
+-              return -EINVAL;
+-
+-      if (!mac_pton(buf, mac))
+-              return -EINVAL;
+-
+-      if (index)
+-              eth_addr_add(mac, index);
+-
+-      ether_addr_copy(buf, mac);
+-
+-      return 0;
+-}
+-
+-static int u_boot_env_add_cells(struct u_boot_env *priv, uint8_t *buf,
+-                              size_t data_offset, size_t data_len)
+-{
+-      struct nvmem_device *nvmem = priv->nvmem;
+-      struct device *dev = priv->dev;
+-      char *data = buf + data_offset;
+-      char *var, *value, *eq;
+-
+-      for (var = data;
+-           var < data + data_len && *var;
+-           var = value + strlen(value) + 1) {
+-              struct nvmem_cell_info info = {};
+-
+-              eq = strchr(var, '=');
+-              if (!eq)
+-                      break;
+-              *eq = '\0';
+-              value = eq + 1;
+-
+-              info.name = devm_kstrdup(dev, var, GFP_KERNEL);
+-              if (!info.name)
+-                      return -ENOMEM;
+-              info.offset = data_offset + value - data;
+-              info.bytes = strlen(value);
+-              info.np = of_get_child_by_name(dev->of_node, info.name);
+-              if (!strcmp(var, "ethaddr")) {
+-                      info.raw_len = strlen(value);
+-                      info.bytes = ETH_ALEN;
+-                      info.read_post_process = u_boot_env_read_post_process_ethaddr;
+-              }
+-
+-              nvmem_add_one_cell(nvmem, &info);
+-      }
+-
+-      return 0;
+-}
+-
+-static int u_boot_env_parse(struct u_boot_env *priv)
+-{
+-      struct nvmem_device *nvmem = priv->nvmem;
+-      struct device *dev = priv->dev;
+-      size_t crc32_data_offset;
+-      size_t crc32_data_len;
+-      size_t crc32_offset;
+-      __le32 *crc32_addr;
+-      size_t data_offset;
+-      size_t data_len;
+-      size_t dev_size;
+-      uint32_t crc32;
+-      uint32_t calc;
+-      uint8_t *buf;
+-      int bytes;
+-      int err;
+-
+-      dev_size = nvmem_dev_size(nvmem);
+-
+-      buf = kzalloc(dev_size, GFP_KERNEL);
+-      if (!buf) {
+-              err = -ENOMEM;
+-              goto err_out;
+-      }
+-
+-      bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
+-      if (bytes < 0) {
+-              err = bytes;
+-              goto err_kfree;
+-      } else if (bytes != dev_size) {
+-              err = -EIO;
+-              goto err_kfree;
+-      }
+-
+-      switch (priv->format) {
+-      case U_BOOT_FORMAT_SINGLE:
+-              crc32_offset = offsetof(struct u_boot_env_image_single, crc32);
+-              crc32_data_offset = offsetof(struct u_boot_env_image_single, data);
+-              data_offset = offsetof(struct u_boot_env_image_single, data);
+-              break;
+-      case U_BOOT_FORMAT_REDUNDANT:
+-              crc32_offset = offsetof(struct u_boot_env_image_redundant, crc32);
+-              crc32_data_offset = offsetof(struct u_boot_env_image_redundant, data);
+-              data_offset = offsetof(struct u_boot_env_image_redundant, data);
+-              break;
+-      case U_BOOT_FORMAT_BROADCOM:
+-              crc32_offset = offsetof(struct u_boot_env_image_broadcom, crc32);
+-              crc32_data_offset = offsetof(struct u_boot_env_image_broadcom, data);
+-              data_offset = offsetof(struct u_boot_env_image_broadcom, data);
+-              break;
+-      }
+-
+-      if (dev_size < data_offset) {
+-              dev_err(dev, "Device too small for u-boot-env\n");
+-              err = -EIO;
+-              goto err_kfree;
+-      }
+-
+-      crc32_addr = (__le32 *)(buf + crc32_offset);
+-      crc32 = le32_to_cpu(*crc32_addr);
+-      crc32_data_len = dev_size - crc32_data_offset;
+-      data_len = dev_size - data_offset;
+-
+-      calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
+-      if (calc != crc32) {
+-              dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
+-              err = -EINVAL;
+-              goto err_kfree;
+-      }
+-
+-      buf[dev_size - 1] = '\0';
+-      err = u_boot_env_add_cells(priv, buf, data_offset, data_len);
+-
+-err_kfree:
+-      kfree(buf);
+-err_out:
+-      return err;
+-}
+-
+ static int u_boot_env_probe(struct platform_device *pdev)
+ {
+       struct nvmem_config config = {
+@@ -235,7 +74,7 @@ static int u_boot_env_probe(struct platf
+       if (IS_ERR(priv->nvmem))
+               return PTR_ERR(priv->nvmem);
+-      return u_boot_env_parse(priv);
++      return u_boot_env_parse(dev, priv->nvmem, priv->format);
+ }
+ static const struct of_device_id u_boot_env_of_match_table[] = {
diff --git a/target/linux/generic/backport-6.6/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch b/target/linux/generic/backport-6.6/831-v6.7-rtc-rtc7301-Support-byte-addressed-IO.patch
new file mode 100644 (file)
index 0000000..ddda6e4
--- /dev/null
@@ -0,0 +1,93 @@
+From edd25a77e69b7c546c28077e5dffe72c54c0afe8 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Thu, 21 Sep 2023 22:18:12 +0200
+Subject: [PATCH 2/4] rtc: rtc7301: Support byte-addressed IO
+
+The old RTC7301 driver in OpenWrt used byte access, but the
+current mainline Linux driver uses 32bit word access.
+
+Make this configurable using device properties using the
+standard property "reg-io-width" in e.g. device tree.
+
+This is needed for the USRobotics USR8200 which has the
+chip connected using byte accesses.
+
+Debugging and testing by Howard Harte.
+
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/rtc/rtc-r7301.c | 35 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 33 insertions(+), 2 deletions(-)
+
+--- a/drivers/rtc/rtc-r7301.c
++++ b/drivers/rtc/rtc-r7301.c
+@@ -14,6 +14,7 @@
+ #include <linux/module.h>
+ #include <linux/mod_devicetable.h>
+ #include <linux/delay.h>
++#include <linux/property.h>
+ #include <linux/regmap.h>
+ #include <linux/platform_device.h>
+ #include <linux/rtc.h>
+@@ -55,12 +56,23 @@ struct rtc7301_priv {
+       u8 bank;
+ };
+-static const struct regmap_config rtc7301_regmap_config = {
++/*
++ * When the device is memory-mapped, some platforms pack the registers into
++ * 32-bit access using the lower 8 bits at each 4-byte stride, while others
++ * expose them as simply consecutive bytes.
++ */
++static const struct regmap_config rtc7301_regmap_32_config = {
+       .reg_bits = 32,
+       .val_bits = 8,
+       .reg_stride = 4,
+ };
++static const struct regmap_config rtc7301_regmap_8_config = {
++      .reg_bits = 8,
++      .val_bits = 8,
++      .reg_stride = 1,
++};
++
+ static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg)
+ {
+       int reg_stride = regmap_get_reg_stride(priv->regmap);
+@@ -356,7 +368,9 @@ static int __init rtc7301_rtc_probe(stru
+       void __iomem *regs;
+       struct rtc7301_priv *priv;
+       struct rtc_device *rtc;
++      static const struct regmap_config *mapconf;
+       int ret;
++      u32 val;
+       priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+@@ -366,8 +380,25 @@ static int __init rtc7301_rtc_probe(stru
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
++      ret = device_property_read_u32(&dev->dev, "reg-io-width", &val);
++      if (ret)
++              /* Default to 32bit accesses */
++              val = 4;
++
++      switch (val) {
++      case 1:
++              mapconf = &rtc7301_regmap_8_config;
++              break;
++      case 4:
++              mapconf = &rtc7301_regmap_32_config;
++              break;
++      default:
++              dev_err(&dev->dev, "invalid reg-io-width %d\n", val);
++              return -EINVAL;
++      }
++
+       priv->regmap = devm_regmap_init_mmio(&dev->dev, regs,
+-                                           &rtc7301_regmap_config);
++                                           mapconf);
+       if (IS_ERR(priv->regmap))
+               return PTR_ERR(priv->regmap);
diff --git a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch
new file mode 100644 (file)
index 0000000..0728720
--- /dev/null
@@ -0,0 +1,82 @@
+From 49e5663b505070424e18099841943f34342aa405 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Sun, 24 Sep 2023 01:09:01 +0200
+Subject: [PATCH] net: phy: amd: Support the Altima AMI101L
+
+The Altima AC101L is obviously compatible with the AMD PHY,
+as seen by reading the datasheet.
+
+Datasheet: https://docs.broadcom.com/doc/AC101L-DS05-405-RDS.pdf
+
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/net/phy/Kconfig |  4 ++--
+ drivers/net/phy/amd.c   | 33 +++++++++++++++++++++++----------
+ 2 files changed, 25 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -74,9 +74,9 @@ config AIR_EN8811H_PHY
+         Currently supports the Airoha EN8811H PHY.
+ config AMD_PHY
+-      tristate "AMD PHYs"
++      tristate "AMD and Altima PHYs"
+       help
+-        Currently supports the am79c874
++        Currently supports the AMD am79c874 and Altima AC101L.
+ config MESON_GXL_PHY
+       tristate "Amlogic Meson GXL Internal PHY"
+--- a/drivers/net/phy/amd.c
++++ b/drivers/net/phy/amd.c
+@@ -13,6 +13,7 @@
+ #include <linux/mii.h>
+ #include <linux/phy.h>
++#define PHY_ID_AC101L         0x00225520
+ #define PHY_ID_AM79C874               0x0022561b
+ #define MII_AM79C_IR          17      /* Interrupt Status/Control Register */
+@@ -87,19 +88,31 @@ static irqreturn_t am79c_handle_interrup
+       return IRQ_HANDLED;
+ }
+-static struct phy_driver am79c_driver[] = { {
+-      .phy_id         = PHY_ID_AM79C874,
+-      .name           = "AM79C874",
+-      .phy_id_mask    = 0xfffffff0,
+-      /* PHY_BASIC_FEATURES */
+-      .config_init    = am79c_config_init,
+-      .config_intr    = am79c_config_intr,
+-      .handle_interrupt = am79c_handle_interrupt,
+-} };
++static struct phy_driver am79c_drivers[] = {
++      {
++              .phy_id         = PHY_ID_AM79C874,
++              .name           = "AM79C874",
++              .phy_id_mask    = 0xfffffff0,
++              /* PHY_BASIC_FEATURES */
++              .config_init    = am79c_config_init,
++              .config_intr    = am79c_config_intr,
++              .handle_interrupt = am79c_handle_interrupt,
++      },
++      {
++              .phy_id         = PHY_ID_AC101L,
++              .name           = "AC101L",
++              .phy_id_mask    = 0xfffffff0,
++              /* PHY_BASIC_FEATURES */
++              .config_init    = am79c_config_init,
++              .config_intr    = am79c_config_intr,
++              .handle_interrupt = am79c_handle_interrupt,
++      },
++};
+-module_phy_driver(am79c_driver);
++module_phy_driver(am79c_drivers);
+ static struct mdio_device_id __maybe_unused amd_tbl[] = {
++      { PHY_ID_AC101L, 0xfffffff0 },
+       { PHY_ID_AM79C874, 0xfffffff0 },
+       { }
+ };
diff --git a/target/linux/generic/backport-6.6/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch b/target/linux/generic/backport-6.6/833-v6.8-leds-core-Add-more-colors-from-DT-bindings-to-led_co.patch.patch
new file mode 100644 (file)
index 0000000..b71df6f
--- /dev/null
@@ -0,0 +1,29 @@
+From a067943129b4ec6b835e02cfd5fbef01093c1471 Mon Sep 17 00:00:00 2001
+From: Ondrej Jirman <megi@xff.cz>
+Date: Sun, 8 Oct 2023 16:40:13 +0200
+Subject: [PATCH] leds: core: Add more colors from DT bindings to led_colors
+
+The colors are already part of DT bindings. Make sure the kernel is
+able to convert them to strings.
+
+Signed-off-by: Ondrej Jirman <megi@xff.cz>
+Link: https://lore.kernel.org/r/20231008144014.1180334-1-megi@xff.cz
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/led-core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/leds/led-core.c
++++ b/drivers/leds/led-core.c
+@@ -36,6 +36,11 @@ const char * const led_colors[LED_COLOR_
+       [LED_COLOR_ID_IR] = "ir",
+       [LED_COLOR_ID_MULTI] = "multicolor",
+       [LED_COLOR_ID_RGB] = "rgb",
++      [LED_COLOR_ID_PURPLE] = "purple",
++      [LED_COLOR_ID_ORANGE] = "orange",
++      [LED_COLOR_ID_PINK] = "pink",
++      [LED_COLOR_ID_CYAN] = "cyan",
++      [LED_COLOR_ID_LIME] = "lime",
+ };
+ EXPORT_SYMBOL_GPL(led_colors);
diff --git a/target/linux/generic/backport-6.6/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch b/target/linux/generic/backport-6.6/834-v6.8-leds-trigger-netdev-Extend-speeds-up-to-10G.patch
new file mode 100644 (file)
index 0000000..4571d7d
--- /dev/null
@@ -0,0 +1,111 @@
+From bc8e1da69a68d9871773b657d18400a7941cbdef Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 28 Nov 2023 04:00:10 +0000
+Subject: [PATCH] leds: trigger: netdev: Extend speeds up to 10G
+
+Add 2.5G, 5G and 10G as available speeds to the netdev LED trigger.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/99e7d3304c6bba7f4863a4a80764a869855f2085.1701143925.git.daniel@makrotopia.org
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/trigger/ledtrig-netdev.c | 32 ++++++++++++++++++++++++++-
+ include/linux/leds.h                  |  3 +++
+ 2 files changed, 34 insertions(+), 1 deletion(-)
+
+--- a/drivers/leds/trigger/ledtrig-netdev.c
++++ b/drivers/leds/trigger/ledtrig-netdev.c
+@@ -99,6 +99,18 @@ static void set_baseline_state(struct le
+                   trigger_data->link_speed == SPEED_1000)
+                       blink_on = true;
++              if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) &&
++                  trigger_data->link_speed == SPEED_2500)
++                      blink_on = true;
++
++              if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) &&
++                  trigger_data->link_speed == SPEED_5000)
++                      blink_on = true;
++
++              if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) &&
++                  trigger_data->link_speed == SPEED_10000)
++                      blink_on = true;
++
+               if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
+                   trigger_data->duplex == DUPLEX_HALF)
+                       blink_on = true;
+@@ -289,6 +301,9 @@ static ssize_t netdev_led_attr_show(stru
+       case TRIGGER_NETDEV_LINK_10:
+       case TRIGGER_NETDEV_LINK_100:
+       case TRIGGER_NETDEV_LINK_1000:
++      case TRIGGER_NETDEV_LINK_2500:
++      case TRIGGER_NETDEV_LINK_5000:
++      case TRIGGER_NETDEV_LINK_10000:
+       case TRIGGER_NETDEV_HALF_DUPLEX:
+       case TRIGGER_NETDEV_FULL_DUPLEX:
+       case TRIGGER_NETDEV_TX:
+@@ -319,6 +334,9 @@ static ssize_t netdev_led_attr_store(str
+       case TRIGGER_NETDEV_LINK_10:
+       case TRIGGER_NETDEV_LINK_100:
+       case TRIGGER_NETDEV_LINK_1000:
++      case TRIGGER_NETDEV_LINK_2500:
++      case TRIGGER_NETDEV_LINK_5000:
++      case TRIGGER_NETDEV_LINK_10000:
+       case TRIGGER_NETDEV_HALF_DUPLEX:
+       case TRIGGER_NETDEV_FULL_DUPLEX:
+       case TRIGGER_NETDEV_TX:
+@@ -337,7 +355,10 @@ static ssize_t netdev_led_attr_store(str
+       if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
+           (test_bit(TRIGGER_NETDEV_LINK_10, &mode) ||
+            test_bit(TRIGGER_NETDEV_LINK_100, &mode) ||
+-           test_bit(TRIGGER_NETDEV_LINK_1000, &mode)))
++           test_bit(TRIGGER_NETDEV_LINK_1000, &mode) ||
++           test_bit(TRIGGER_NETDEV_LINK_2500, &mode) ||
++           test_bit(TRIGGER_NETDEV_LINK_5000, &mode) ||
++           test_bit(TRIGGER_NETDEV_LINK_10000, &mode)))
+               return -EINVAL;
+       cancel_delayed_work_sync(&trigger_data->work);
+@@ -367,6 +388,9 @@ DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETD
+ DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);
+ DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);
+ DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
++DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500);
++DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000);
++DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000);
+ DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX);
+ DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX);
+ DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
+@@ -425,6 +449,9 @@ static struct attribute *netdev_trig_att
+       &dev_attr_link_10.attr,
+       &dev_attr_link_100.attr,
+       &dev_attr_link_1000.attr,
++      &dev_attr_link_2500.attr,
++      &dev_attr_link_5000.attr,
++      &dev_attr_link_10000.attr,
+       &dev_attr_full_duplex.attr,
+       &dev_attr_half_duplex.attr,
+       &dev_attr_rx.attr,
+@@ -522,6 +549,9 @@ static void netdev_trig_work(struct work
+                        test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
+                        test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
+                        test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) ||
++                       test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) ||
++                       test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) ||
++                       test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) ||
+                        test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) ||
+                        test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode);
+               interval = jiffies_to_msecs(
+--- a/include/linux/leds.h
++++ b/include/linux/leds.h
+@@ -586,6 +586,9 @@ enum led_trigger_netdev_modes {
+       TRIGGER_NETDEV_LINK_10,
+       TRIGGER_NETDEV_LINK_100,
+       TRIGGER_NETDEV_LINK_1000,
++      TRIGGER_NETDEV_LINK_2500,
++      TRIGGER_NETDEV_LINK_5000,
++      TRIGGER_NETDEV_LINK_10000,
+       TRIGGER_NETDEV_HALF_DUPLEX,
+       TRIGGER_NETDEV_FULL_DUPLEX,
+       TRIGGER_NETDEV_TX,
diff --git a/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch b/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch
new file mode 100644 (file)
index 0000000..1fe0e3f
--- /dev/null
@@ -0,0 +1,98 @@
+From 7ae215ee7bb855f13c80565470fc7f67db4ba82f Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 25 Jan 2024 21:36:59 +0100
+Subject: [PATCH 3/5] net: phy: add support for PHY LEDs polarity modes
+
+Add support for PHY LEDs polarity modes. Some PHY require LED to be set
+to active low to be turned ON. Adds support for this by declaring
+active-low property in DT.
+
+PHY driver needs to declare .led_polarity_set() to configure LED
+polarity modes. Function will pass the index with the LED index and a
+bitmap with all the required modes to set.
+
+Current supported modes are:
+- active-low with the flag PHY_LED_ACTIVE_LOW. LED is set to active-low
+  to turn it ON.
+- inactive-high-impedance with the flag PHY_LED_INACTIVE_HIGH_IMPEDANCE.
+  LED is set to high impedance to turn it OFF.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240125203702.4552-4-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phy_device.c | 16 ++++++++++++++++
+ include/linux/phy.h          | 22 ++++++++++++++++++++++
+ 2 files changed, 38 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -3202,6 +3202,7 @@ static int of_phy_led(struct phy_device
+       struct device *dev = &phydev->mdio.dev;
+       struct led_init_data init_data = {};
+       struct led_classdev *cdev;
++      unsigned long modes = 0;
+       struct phy_led *phyled;
+       u32 index;
+       int err;
+@@ -3219,6 +3220,21 @@ static int of_phy_led(struct phy_device
+       if (index > U8_MAX)
+               return -EINVAL;
++      if (of_property_read_bool(led, "active-low"))
++              set_bit(PHY_LED_ACTIVE_LOW, &modes);
++      if (of_property_read_bool(led, "inactive-high-impedance"))
++              set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes);
++
++      if (modes) {
++              /* Return error if asked to set polarity modes but not supported */
++              if (!phydev->drv->led_polarity_set)
++                      return -EINVAL;
++
++              err = phydev->drv->led_polarity_set(phydev, index, modes);
++              if (err)
++                      return err;
++      }
++
+       phyled->index = index;
+       if (phydev->drv->led_brightness_set)
+               cdev->brightness_set_blocking = phy_led_set_brightness;
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -870,6 +870,15 @@ struct phy_led {
+ #define to_phy_led(d) container_of(d, struct phy_led, led_cdev)
++/* Modes for PHY LED configuration */
++enum phy_led_modes {
++      PHY_LED_ACTIVE_LOW = 0,
++      PHY_LED_INACTIVE_HIGH_IMPEDANCE = 1,
++
++      /* keep it last */
++      __PHY_LED_MODES_NUM,
++};
++
+ /**
+  * struct phy_driver - Driver structure for a particular PHY type
+  *
+@@ -1146,6 +1155,19 @@ struct phy_driver {
+       int (*led_hw_control_get)(struct phy_device *dev, u8 index,
+                                 unsigned long *rules);
++      /**
++       * @led_polarity_set: Set the LED polarity modes
++       * @dev: PHY device which has the LED
++       * @index: Which LED of the PHY device
++       * @modes: bitmap of LED polarity modes
++       *
++       * Configure LED with all the required polarity modes in @modes
++       * to make it correctly turn ON or OFF.
++       *
++       * Returns 0, or an error code.
++       */
++      int (*led_polarity_set)(struct phy_device *dev, int index,
++                              unsigned long modes);
+ };
+ #define to_phy_driver(d) container_of(to_mdio_common_driver(d),               \
+                                     struct phy_driver, mdiodrv)
diff --git a/target/linux/generic/backport-6.6/836-v6.9-net-phy-aquantia-clear-PMD-Global-Transmit-Disable-b.patch b/target/linux/generic/backport-6.6/836-v6.9-net-phy-aquantia-clear-PMD-Global-Transmit-Disable-b.patch
new file mode 100644 (file)
index 0000000..8b54881
--- /dev/null
@@ -0,0 +1,103 @@
+From cffac22c9215f1883d3848c788f9b03656dced27 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Sun, 11 Feb 2024 18:39:19 +0100
+Subject: [PATCH] net: phy: aquantia: clear PMD Global Transmit Disable bit
+ during init
+
+PMD Global Transmit Disable bit should be cleared for normal operation.
+This should be HW default, however I found that on Asus RT-AX89X that uses
+AQR113C PHY and firmware 5.4 this bit is set by default.
+
+With this bit set the AQR cannot achieve a link with its link-partner and
+it took me multiple hours of digging through the vendor GPL source to find
+this out, so lets always clear this bit during .config_init() to avoid a
+situation like this in the future.
+
+aqr107_wait_processor_intensive_op() is moved up because datasheet notes
+that any changes to this bit are processor intensive.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+---
+ drivers/net/phy/aquantia/aquantia_main.c | 57 ++++++++++++++----------
+ 1 file changed, 33 insertions(+), 24 deletions(-)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -473,6 +473,30 @@ static void aqr107_chip_info(struct phy_
+                  fw_major, fw_minor, build_id, prov_id);
+ }
++static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
++{
++      int val, err;
++
++      /* The datasheet notes to wait at least 1ms after issuing a
++       * processor intensive operation before checking.
++       * We cannot use the 'sleep_before_read' parameter of read_poll_timeout
++       * because that just determines the maximum time slept, not the minimum.
++       */
++      usleep_range(1000, 5000);
++
++      err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
++                                      VEND1_GLOBAL_GEN_STAT2, val,
++                                      !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG),
++                                      AQR107_OP_IN_PROG_SLEEP,
++                                      AQR107_OP_IN_PROG_TIMEOUT, false);
++      if (err) {
++              phydev_err(phydev, "timeout: processor-intensive MDIO operation\n");
++              return err;
++      }
++
++      return 0;
++}
++
+ static int aqr107_config_init(struct phy_device *phydev)
+ {
+       struct aqr107_priv *priv = phydev->priv;
+@@ -498,6 +522,15 @@ static int aqr107_config_init(struct phy
+       if (!ret)
+               aqr107_chip_info(phydev);
++      ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS,
++                               MDIO_PMD_TXDIS_GLOBAL);
++      if (ret)
++              return ret;
++
++      ret = aqr107_wait_processor_intensive_op(phydev);
++      if (ret)
++              return ret;
++
+       ret = aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
+       if (ret)
+               return ret;
+@@ -580,30 +613,6 @@ static void aqr107_link_change_notify(st
+               phydev_info(phydev, "Aquantia 1000Base-T2 mode active\n");
+ }
+-static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
+-{
+-      int val, err;
+-
+-      /* The datasheet notes to wait at least 1ms after issuing a
+-       * processor intensive operation before checking.
+-       * We cannot use the 'sleep_before_read' parameter of read_poll_timeout
+-       * because that just determines the maximum time slept, not the minimum.
+-       */
+-      usleep_range(1000, 5000);
+-
+-      err = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
+-                                      VEND1_GLOBAL_GEN_STAT2, val,
+-                                      !(val & VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG),
+-                                      AQR107_OP_IN_PROG_SLEEP,
+-                                      AQR107_OP_IN_PROG_TIMEOUT, false);
+-      if (err) {
+-              phydev_err(phydev, "timeout: processor-intensive MDIO operation\n");
+-              return err;
+-      }
+-
+-      return 0;
+-}
+-
+ static int aqr107_get_rate_matching(struct phy_device *phydev,
+                                   phy_interface_t iface)
+ {
diff --git a/target/linux/generic/backport-6.6/837-v6.12-net-phy-aquantia-fix-setting-active_low-bit.patch b/target/linux/generic/backport-6.6/837-v6.12-net-phy-aquantia-fix-setting-active_low-bit.patch
new file mode 100644 (file)
index 0000000..6072e02
--- /dev/null
@@ -0,0 +1,55 @@
+From patchwork Tue Sep 17 13:49:40 2024
+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: 13806176
+X-Patchwork-Delegate: kuba@kernel.org
+Date: Tue, 17 Sep 2024 14:49:40 +0100
+From: Daniel Golle <daniel@makrotopia.org>
+To: Andrew Lunn <andrew@lunn.ch>, Heiner Kallweit <hkallweit1@gmail.com>,
+       Russell King <linux@armlinux.org.uk>,
+       "David S. Miller" <davem@davemloft.net>,
+       Eric Dumazet <edumazet@google.com>,
+       Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
+       Daniel Golle <daniel@makrotopia.org>,
+       Christian Marangi <ansuelsmth@gmail.com>,
+       Bartosz Golaszewski <bartosz.golaszewski@linaro.org>,
+       Robert Marko <robimarko@gmail.com>,
+       Russell King <rmk+kernel@armlinux.org.uk>, netdev@vger.kernel.org,
+       linux-kernel@vger.kernel.org
+Subject: [PATCH net 1/2] net: phy: aquantia: fix setting active_low bit
+Message-ID: 
+ <ab963584b0a7e3b4dac39472a4b82ca264d79630.1726580902.git.daniel@makrotopia.org>
+Precedence: bulk
+X-Mailing-List: netdev@vger.kernel.org
+List-Id: <netdev.vger.kernel.org>
+List-Subscribe: <mailto:netdev+subscribe@vger.kernel.org>
+List-Unsubscribe: <mailto:netdev+unsubscribe@vger.kernel.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+X-Patchwork-Delegate: kuba@kernel.org
+
+phy_modify_mmd was used wrongly in aqr_phy_led_active_low_set() resulting
+in a no-op instead of setting the VEND1_GLOBAL_LED_DRIVE_VDD bit.
+Correctly set VEND1_GLOBAL_LED_DRIVE_VDD bit.
+
+Fixes: 61578f679378 ("net: phy: aquantia: add support for PHY LEDs")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/aquantia/aquantia_leds.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/aquantia/aquantia_leds.c
++++ b/drivers/net/phy/aquantia/aquantia_leds.c
+@@ -120,7 +120,8 @@ int aqr_phy_led_hw_control_set(struct ph
+ int aqr_phy_led_active_low_set(struct phy_device *phydev, int index, bool enable)
+ {
+       return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index),
+-                            VEND1_GLOBAL_LED_DRIVE_VDD, enable);
++                            VEND1_GLOBAL_LED_DRIVE_VDD,
++                            enable ? VEND1_GLOBAL_LED_DRIVE_VDD : 0);
+ }
+ int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes)
diff --git a/target/linux/generic/backport-6.6/838-v6.12-net-phy-aquantia-fix-applying-active_low-bit-after-reset.patch b/target/linux/generic/backport-6.6/838-v6.12-net-phy-aquantia-fix-applying-active_low-bit-after-reset.patch
new file mode 100644 (file)
index 0000000..5c3494a
--- /dev/null
@@ -0,0 +1,72 @@
+From patchwork Tue Sep 17 13:49:55 2024
+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: 13806177
+X-Patchwork-Delegate: kuba@kernel.org
+Date: Tue, 17 Sep 2024 14:49:55 +0100
+From: Daniel Golle <daniel@makrotopia.org>
+To: Andrew Lunn <andrew@lunn.ch>, Heiner Kallweit <hkallweit1@gmail.com>,
+       Russell King <linux@armlinux.org.uk>,
+       "David S. Miller" <davem@davemloft.net>,
+       Eric Dumazet <edumazet@google.com>,
+       Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
+       Daniel Golle <daniel@makrotopia.org>,
+       Christian Marangi <ansuelsmth@gmail.com>,
+       Bartosz Golaszewski <bartosz.golaszewski@linaro.org>,
+       Robert Marko <robimarko@gmail.com>,
+       Russell King <rmk+kernel@armlinux.org.uk>, netdev@vger.kernel.org,
+       linux-kernel@vger.kernel.org
+Subject: [PATCH net 2/2] net: phy: aquantia: fix applying active_low bit
+ after reset
+Message-ID: 
+ <9b1f0cd91f4cda54c8be56b4fe780480baf4aa0f.1726580902.git.daniel@makrotopia.org>
+References: 
+ <ab963584b0a7e3b4dac39472a4b82ca264d79630.1726580902.git.daniel@makrotopia.org>
+Precedence: bulk
+X-Mailing-List: netdev@vger.kernel.org
+List-Id: <netdev.vger.kernel.org>
+List-Subscribe: <mailto:netdev+subscribe@vger.kernel.org>
+List-Unsubscribe: <mailto:netdev+unsubscribe@vger.kernel.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+In-Reply-To: 
+ <ab963584b0a7e3b4dac39472a4b82ca264d79630.1726580902.git.daniel@makrotopia.org>
+X-Patchwork-Delegate: kuba@kernel.org
+
+for_each_set_bit was used wrongly in aqr107_config_init() when iterating
+over LEDs. Drop misleading 'index' variable and call
+aqr_phy_led_active_low_set() for each set bit representing an LED which
+is driven by VDD instead of GND pin.
+
+Fixes: 61578f679378 ("net: phy: aquantia: add support for PHY LEDs")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/aquantia/aquantia_main.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -501,7 +501,7 @@ static int aqr107_config_init(struct phy
+ {
+       struct aqr107_priv *priv = phydev->priv;
+       u32 led_active_low;
+-      int ret, index = 0;
++      int ret;
+       /* Check that the PHY interface type is compatible */
+       if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
+@@ -537,10 +537,9 @@ static int aqr107_config_init(struct phy
+       /* Restore LED polarity state after reset */
+       for_each_set_bit(led_active_low, &priv->leds_active_low, AQR_MAX_LEDS) {
+-              ret = aqr_phy_led_active_low_set(phydev, index, led_active_low);
++              ret = aqr_phy_led_active_low_set(phydev, led_active_low, true);
+               if (ret)
+                       return ret;
+-              index++;
+       }
+       return 0;
diff --git a/target/linux/generic/backport-6.6/839-v6.13-net-phy-aquantia-allow-forcing-order-of-MDI-pairs.patch b/target/linux/generic/backport-6.6/839-v6.13-net-phy-aquantia-allow-forcing-order-of-MDI-pairs.patch
new file mode 100644 (file)
index 0000000..c1655ce
--- /dev/null
@@ -0,0 +1,107 @@
+From a2e1ba275eae96a8171deb19e9c7c2f5978fee7b Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Fri, 4 Oct 2024 17:18:16 +0100
+Subject: [PATCH] net: phy: aquantia: allow forcing order of MDI pairs
+
+Despite supporting Auto MDI-X, it looks like Aquantia only supports
+swapping pair (1,2) with pair (3,6) like it used to be for MDI-X on
+100MBit/s networks.
+
+When all 4 pairs are in use (for 1000MBit/s or faster) the link does not
+come up with pair order is not configured correctly, either using
+MDI_CFG pin or using the "PMA Receive Reserved Vendor Provisioning 1"
+register.
+
+Normally, the order of MDI pairs being either ABCD or DCBA is configured
+by pulling the MDI_CFG pin.
+
+However, some hardware designs require overriding the value configured
+by that bootstrap pin. The PHY allows doing that by setting a bit in
+"PMA Receive Reserved Vendor Provisioning 1" register which allows
+ignoring the state of the MDI_CFG pin and another bit configuring
+whether the order of MDI pairs should be normal (ABCD) or reverse
+(DCBA). Pair polarity is not affected and remains identical in both
+settings.
+
+Introduce property "marvell,mdi-cfg-order" which allows forcing either
+normal or reverse order of the MDI pairs from DT.
+
+If the property isn't present, the behavior is unchanged and MDI pair
+order configuration is untouched (ie. either the result of MDI_CFG pin
+pull-up/pull-down, or pair order override already configured by the
+bootloader before Linux is started).
+
+Forcing normal pair order is required on the Adtran SDG-8733A Wi-Fi 7
+residential gateway.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/9ed760ff87d5fc456f31e407ead548bbb754497d.1728058550.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/aquantia/aquantia_main.c | 33 ++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -11,6 +11,7 @@
+ #include <linux/module.h>
+ #include <linux/delay.h>
+ #include <linux/bitfield.h>
++#include <linux/of.h>
+ #include <linux/phy.h>
+ #include "aquantia.h"
+@@ -70,6 +71,11 @@
+ #define MDIO_AN_TX_VEND_INT_MASK2             0xd401
+ #define MDIO_AN_TX_VEND_INT_MASK2_LINK                BIT(0)
++#define PMAPMD_RSVD_VEND_PROV                 0xe400
++#define PMAPMD_RSVD_VEND_PROV_MDI_CONF                GENMASK(1, 0)
++#define PMAPMD_RSVD_VEND_PROV_MDI_REVERSE     BIT(0)
++#define PMAPMD_RSVD_VEND_PROV_MDI_FORCE               BIT(1)
++
+ #define MDIO_AN_RX_LP_STAT1                   0xe820
+ #define MDIO_AN_RX_LP_STAT1_1000BASET_FULL    BIT(15)
+ #define MDIO_AN_RX_LP_STAT1_1000BASET_HALF    BIT(14)
+@@ -497,6 +503,29 @@ static int aqr107_wait_processor_intensi
+       return 0;
+ }
++static int aqr107_config_mdi(struct phy_device *phydev)
++{
++      struct device_node *np = phydev->mdio.dev.of_node;
++      u32 mdi_conf;
++      int ret;
++
++      ret = of_property_read_u32(np, "marvell,mdi-cfg-order", &mdi_conf);
++
++      /* Do nothing in case property "marvell,mdi-cfg-order" is not present */
++      if (ret == -ENOENT)
++              return 0;
++
++      if (ret)
++              return ret;
++
++      if (mdi_conf & ~PMAPMD_RSVD_VEND_PROV_MDI_REVERSE)
++              return -EINVAL;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_RSVD_VEND_PROV,
++                            PMAPMD_RSVD_VEND_PROV_MDI_CONF,
++                            mdi_conf | PMAPMD_RSVD_VEND_PROV_MDI_FORCE);
++}
++
+ static int aqr107_config_init(struct phy_device *phydev)
+ {
+       struct aqr107_priv *priv = phydev->priv;
+@@ -535,6 +564,10 @@ static int aqr107_config_init(struct phy
+       if (ret)
+               return ret;
++      ret = aqr107_config_mdi(phydev);
++      if (ret)
++              return ret;
++
+       /* Restore LED polarity state after reset */
+       for_each_set_bit(led_active_low, &priv->leds_active_low, AQR_MAX_LEDS) {
+               ret = aqr_phy_led_active_low_set(phydev, led_active_low, true);
diff --git a/target/linux/generic/backport-6.6/840-v6.13-net-phy-aquantia-fix-return-value-check-in-aqr107_co.patch b/target/linux/generic/backport-6.6/840-v6.13-net-phy-aquantia-fix-return-value-check-in-aqr107_co.patch
new file mode 100644 (file)
index 0000000..73be78a
--- /dev/null
@@ -0,0 +1,31 @@
+From ce21b8fb255ebf0b49913fb4c62741d7eb05c6f6 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Fri, 11 Oct 2024 22:28:43 +0100
+Subject: [PATCH] net: phy: aquantia: fix return value check in
+ aqr107_config_mdi()
+
+of_property_read_u32() returns -EINVAL in case the property cannot be
+found rather than -ENOENT. Fix the check to not abort probing in case
+of the property being missing, and also in case CONFIG_OF is not set
+which will result in -ENOSYS.
+
+Fixes: a2e1ba275eae ("net: phy: aquantia: allow forcing order of MDI pairs")
+Reported-by: Jon Hunter <jonathanh@nvidia.com>
+Closes: https://lore.kernel.org/all/114b4c03-5d16-42ed-945d-cf78eabea12b@nvidia.com/
+Suggested-by: Hans-Frieder Vogt <hfdevel@gmx.net>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/aquantia/aquantia_main.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -512,7 +512,7 @@ static int aqr107_config_mdi(struct phy_
+       ret = of_property_read_u32(np, "marvell,mdi-cfg-order", &mdi_conf);
+       /* Do nothing in case property "marvell,mdi-cfg-order" is not present */
+-      if (ret == -ENOENT)
++      if (ret == -EINVAL || ret == -ENOSYS)
+               return 0;
+       if (ret)
diff --git a/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch b/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch
new file mode 100644 (file)
index 0000000..729a315
--- /dev/null
@@ -0,0 +1,53 @@
+From a274465cc3bef2dfd9c9ea5100848dda0a8641e1 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 10 Oct 2024 13:54:19 +0100
+Subject: [PATCH 1/4] net: phy: support 'active-high' property for PHY LEDs
+
+In addition to 'active-low' and 'inactive-high-impedance' also
+support 'active-high' property for PHY LED pin configuration.
+As only either 'active-high' or 'active-low' can be set at the
+same time, WARN and return an error in case both are set.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/91598487773d768f254d5faf06cf65b13e972f0e.1728558223.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/phy_device.c | 6 ++++++
+ include/linux/phy.h          | 5 +++--
+ 2 files changed, 9 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -3220,11 +3220,17 @@ static int of_phy_led(struct phy_device
+       if (index > U8_MAX)
+               return -EINVAL;
++      if (of_property_read_bool(led, "active-high"))
++              set_bit(PHY_LED_ACTIVE_HIGH, &modes);
+       if (of_property_read_bool(led, "active-low"))
+               set_bit(PHY_LED_ACTIVE_LOW, &modes);
+       if (of_property_read_bool(led, "inactive-high-impedance"))
+               set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes);
++      if (WARN_ON(modes & BIT(PHY_LED_ACTIVE_LOW) &&
++                  modes & BIT(PHY_LED_ACTIVE_HIGH)))
++              return -EINVAL;
++
+       if (modes) {
+               /* Return error if asked to set polarity modes but not supported */
+               if (!phydev->drv->led_polarity_set)
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -872,8 +872,9 @@ struct phy_led {
+ /* Modes for PHY LED configuration */
+ enum phy_led_modes {
+-      PHY_LED_ACTIVE_LOW = 0,
+-      PHY_LED_INACTIVE_HIGH_IMPEDANCE = 1,
++      PHY_LED_ACTIVE_HIGH = 0,
++      PHY_LED_ACTIVE_LOW = 1,
++      PHY_LED_INACTIVE_HIGH_IMPEDANCE = 2,
+       /* keep it last */
+       __PHY_LED_MODES_NUM,
diff --git a/target/linux/generic/backport-6.6/842-v6.13-net-phy-aquantia-correctly-describe-LED-polarity-ove.patch b/target/linux/generic/backport-6.6/842-v6.13-net-phy-aquantia-correctly-describe-LED-polarity-ove.patch
new file mode 100644 (file)
index 0000000..f26bb82
--- /dev/null
@@ -0,0 +1,108 @@
+From 9d55e68b19f222e6334ef4021c5527998f5ab537 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 10 Oct 2024 13:55:00 +0100
+Subject: [PATCH 2/4] net: phy: aquantia: correctly describe LED polarity
+ override
+
+Use newly defined 'active-high' property to set the
+VEND1_GLOBAL_LED_DRIVE_VDD bit and let 'active-low' clear that bit. This
+reflects the technical reality which was inverted in the previous
+description in which the 'active-low' property was used to actually set
+the VEND1_GLOBAL_LED_DRIVE_VDD bit, which means that VDD (ie. supply
+voltage) of the LED is driven rather than GND.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/86a413b4387c42dcb54f587cc2433a06f16aae83.1728558223.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/aquantia/aquantia.h      |  1 +
+ drivers/net/phy/aquantia/aquantia_leds.c | 19 ++++++++++++++-----
+ drivers/net/phy/aquantia/aquantia_main.c | 12 +++++++++---
+ 3 files changed, 24 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/phy/aquantia/aquantia.h
++++ b/drivers/net/phy/aquantia/aquantia.h
+@@ -169,6 +169,7 @@ static const struct aqr107_hw_stat aqr10
+ struct aqr107_priv {
+       u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
+       unsigned long leds_active_low;
++      unsigned long leds_active_high;
+ };
+ #if IS_REACHABLE(CONFIG_HWMON)
+--- a/drivers/net/phy/aquantia/aquantia_leds.c
++++ b/drivers/net/phy/aquantia/aquantia_leds.c
+@@ -121,13 +121,13 @@ int aqr_phy_led_active_low_set(struct ph
+ {
+       return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index),
+                             VEND1_GLOBAL_LED_DRIVE_VDD,
+-                            enable ? VEND1_GLOBAL_LED_DRIVE_VDD : 0);
++                            enable ? 0 : VEND1_GLOBAL_LED_DRIVE_VDD);
+ }
+ int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes)
+ {
++      bool force_active_low = false, force_active_high = false;
+       struct aqr107_priv *priv = phydev->priv;
+-      bool active_low = false;
+       u32 mode;
+       if (index >= AQR_MAX_LEDS)
+@@ -136,7 +136,10 @@ int aqr_phy_led_polarity_set(struct phy_
+       for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
+               switch (mode) {
+               case PHY_LED_ACTIVE_LOW:
+-                      active_low = true;
++                      force_active_low = true;
++                      break;
++              case PHY_LED_ACTIVE_HIGH:
++                      force_active_high = true;
+                       break;
+               default:
+                       return -EINVAL;
+@@ -144,8 +147,14 @@ int aqr_phy_led_polarity_set(struct phy_
+       }
+       /* Save LED driver vdd state to restore on SW reset */
+-      if (active_low)
++      if (force_active_low)
+               priv->leds_active_low |= BIT(index);
+-      return aqr_phy_led_active_low_set(phydev, index, active_low);
++      if (force_active_high)
++              priv->leds_active_high |= BIT(index);
++
++      if (force_active_high || force_active_low)
++              return aqr_phy_led_active_low_set(phydev, index, force_active_low);
++
++      unreachable();
+ }
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -529,7 +529,7 @@ static int aqr107_config_mdi(struct phy_
+ static int aqr107_config_init(struct phy_device *phydev)
+ {
+       struct aqr107_priv *priv = phydev->priv;
+-      u32 led_active_low;
++      u32 led_idx;
+       int ret;
+       /* Check that the PHY interface type is compatible */
+@@ -569,8 +569,14 @@ static int aqr107_config_init(struct phy
+               return ret;
+       /* Restore LED polarity state after reset */
+-      for_each_set_bit(led_active_low, &priv->leds_active_low, AQR_MAX_LEDS) {
+-              ret = aqr_phy_led_active_low_set(phydev, led_active_low, true);
++      for_each_set_bit(led_idx, &priv->leds_active_low, AQR_MAX_LEDS) {
++              ret = aqr_phy_led_active_low_set(phydev, led_idx, true);
++              if (ret)
++                      return ret;
++      }
++
++      for_each_set_bit(led_idx, &priv->leds_active_high, AQR_MAX_LEDS) {
++              ret = aqr_phy_led_active_low_set(phydev, led_idx, false);
+               if (ret)
+                       return ret;
+       }
diff --git a/target/linux/generic/backport-6.6/843-v6.13-net-phy-mxl-gpy-add-basic-LED-support.patch b/target/linux/generic/backport-6.6/843-v6.13-net-phy-mxl-gpy-add-basic-LED-support.patch
new file mode 100644 (file)
index 0000000..2ceaa0a
--- /dev/null
@@ -0,0 +1,332 @@
+From 78997e9a5e4d8a4df561e083a92c91ae23010e07 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 1 Oct 2024 01:17:18 +0100
+Subject: [PATCH] net: phy: mxl-gpy: add basic LED support
+
+Add basic support for LEDs connected to MaxLinear GPY2xx and GPY115 PHYs.
+The PHYs allow up to 4 LEDs to be connected.
+Implement controlling LEDs in software as well as netdev trigger offloading
+and LED polarity setup.
+
+The hardware claims to support 16 PWM brightness levels but there is no
+documentation on how to use that feature, hence this is not supported.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/b6ec9050339f8244ff898898a1cecc33b13a48fc.1727741563.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/mxl-gpy.c | 218 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 218 insertions(+)
+
+--- a/drivers/net/phy/mxl-gpy.c
++++ b/drivers/net/phy/mxl-gpy.c
+@@ -38,6 +38,7 @@
+ #define PHY_MIISTAT           0x18    /* MII state */
+ #define PHY_IMASK             0x19    /* interrupt mask */
+ #define PHY_ISTAT             0x1A    /* interrupt status */
++#define PHY_LED                       0x1B    /* LEDs */
+ #define PHY_FWV                       0x1E    /* firmware version */
+ #define PHY_MIISTAT_SPD_MASK  GENMASK(2, 0)
+@@ -61,6 +62,11 @@
+                                PHY_IMASK_ADSC | \
+                                PHY_IMASK_ANC)
++#define GPY_MAX_LEDS          4
++#define PHY_LED_POLARITY(idx) BIT(12 + (idx))
++#define PHY_LED_HWCONTROL(idx)        BIT(8 + (idx))
++#define PHY_LED_ON(idx)               BIT(idx)
++
+ #define PHY_FWV_REL_MASK      BIT(15)
+ #define PHY_FWV_MAJOR_MASK    GENMASK(11, 8)
+ #define PHY_FWV_MINOR_MASK    GENMASK(7, 0)
+@@ -72,6 +78,23 @@
+ #define PHY_MDI_MDI_X_CD      0x1
+ #define PHY_MDI_MDI_X_CROSS   0x0
++/* LED */
++#define VSPEC1_LED(idx)               (1 + (idx))
++#define VSPEC1_LED_BLINKS     GENMASK(15, 12)
++#define VSPEC1_LED_PULSE      GENMASK(11, 8)
++#define VSPEC1_LED_CON                GENMASK(7, 4)
++#define VSPEC1_LED_BLINKF     GENMASK(3, 0)
++
++#define VSPEC1_LED_LINK10     BIT(0)
++#define VSPEC1_LED_LINK100    BIT(1)
++#define VSPEC1_LED_LINK1000   BIT(2)
++#define VSPEC1_LED_LINK2500   BIT(3)
++
++#define VSPEC1_LED_TXACT      BIT(0)
++#define VSPEC1_LED_RXACT      BIT(1)
++#define VSPEC1_LED_COL                BIT(2)
++#define VSPEC1_LED_NO_CON     BIT(3)
++
+ /* SGMII */
+ #define VSPEC1_SGMII_CTRL     0x08
+ #define VSPEC1_SGMII_CTRL_ANEN        BIT(12)         /* Aneg enable */
+@@ -827,6 +850,156 @@ static int gpy115_loopback(struct phy_de
+       return genphy_soft_reset(phydev);
+ }
++static int gpy_led_brightness_set(struct phy_device *phydev,
++                                u8 index, enum led_brightness value)
++{
++      int ret;
++
++      if (index >= GPY_MAX_LEDS)
++              return -EINVAL;
++
++      /* clear HWCONTROL and set manual LED state */
++      ret = phy_modify(phydev, PHY_LED,
++                       ((value == LED_OFF) ? PHY_LED_HWCONTROL(index) : 0) |
++                       PHY_LED_ON(index),
++                       (value == LED_OFF) ? 0 : PHY_LED_ON(index));
++      if (ret)
++              return ret;
++
++      /* ToDo: set PWM brightness */
++
++      /* clear HW LED setup */
++      if (value == LED_OFF)
++              return phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index), 0);
++      else
++              return 0;
++}
++
++static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
++                                               BIT(TRIGGER_NETDEV_LINK_100) |
++                                               BIT(TRIGGER_NETDEV_LINK_1000) |
++                                               BIT(TRIGGER_NETDEV_LINK_2500) |
++                                               BIT(TRIGGER_NETDEV_RX) |
++                                               BIT(TRIGGER_NETDEV_TX));
++
++static int gpy_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                 unsigned long rules)
++{
++      if (index >= GPY_MAX_LEDS)
++              return -EINVAL;
++
++      /* All combinations of the supported triggers are allowed */
++      if (rules & ~supported_triggers)
++              return -EOPNOTSUPP;
++
++      return 0;
++}
++
++static int gpy_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                unsigned long *rules)
++{
++      int val;
++
++      if (index >= GPY_MAX_LEDS)
++              return -EINVAL;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index));
++      if (val < 0)
++              return val;
++
++      if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK10)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_10);
++
++      if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK100)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_100);
++
++      if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK1000)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
++
++      if (FIELD_GET(VSPEC1_LED_CON, val) & VSPEC1_LED_LINK2500)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_2500);
++
++      if (FIELD_GET(VSPEC1_LED_CON, val) == (VSPEC1_LED_LINK10 |
++                                             VSPEC1_LED_LINK100 |
++                                             VSPEC1_LED_LINK1000 |
++                                             VSPEC1_LED_LINK2500))
++              *rules |= BIT(TRIGGER_NETDEV_LINK);
++
++      if (FIELD_GET(VSPEC1_LED_PULSE, val) & VSPEC1_LED_TXACT)
++              *rules |= BIT(TRIGGER_NETDEV_TX);
++
++      if (FIELD_GET(VSPEC1_LED_PULSE, val) & VSPEC1_LED_RXACT)
++              *rules |= BIT(TRIGGER_NETDEV_RX);
++
++      return 0;
++}
++
++static int gpy_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                unsigned long rules)
++{
++      u16 val = 0;
++      int ret;
++
++      if (index >= GPY_MAX_LEDS)
++              return -EINVAL;
++
++      if (rules & BIT(TRIGGER_NETDEV_LINK) ||
++          rules & BIT(TRIGGER_NETDEV_LINK_10))
++              val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK10);
++
++      if (rules & BIT(TRIGGER_NETDEV_LINK) ||
++          rules & BIT(TRIGGER_NETDEV_LINK_100))
++              val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK100);
++
++      if (rules & BIT(TRIGGER_NETDEV_LINK) ||
++          rules & BIT(TRIGGER_NETDEV_LINK_1000))
++              val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK1000);
++
++      if (rules & BIT(TRIGGER_NETDEV_LINK) ||
++          rules & BIT(TRIGGER_NETDEV_LINK_2500))
++              val |= FIELD_PREP(VSPEC1_LED_CON, VSPEC1_LED_LINK2500);
++
++      if (rules & BIT(TRIGGER_NETDEV_TX))
++              val |= FIELD_PREP(VSPEC1_LED_PULSE, VSPEC1_LED_TXACT);
++
++      if (rules & BIT(TRIGGER_NETDEV_RX))
++              val |= FIELD_PREP(VSPEC1_LED_PULSE, VSPEC1_LED_RXACT);
++
++      /* allow RX/TX pulse without link indication */
++      if ((rules & BIT(TRIGGER_NETDEV_TX) || rules & BIT(TRIGGER_NETDEV_RX)) &&
++          !(val & VSPEC1_LED_CON))
++              val |= FIELD_PREP(VSPEC1_LED_PULSE, VSPEC1_LED_NO_CON) | VSPEC1_LED_CON;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index), val);
++      if (ret)
++              return ret;
++
++      return phy_set_bits(phydev, PHY_LED, PHY_LED_HWCONTROL(index));
++}
++
++static int gpy_led_polarity_set(struct phy_device *phydev, int index,
++                              unsigned long modes)
++{
++      bool active_low = false;
++      u32 mode;
++
++      if (index >= GPY_MAX_LEDS)
++              return -EINVAL;
++
++      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++              switch (mode) {
++              case PHY_LED_ACTIVE_LOW:
++                      active_low = true;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      return phy_modify(phydev, PHY_LED, PHY_LED_POLARITY(index),
++                        active_low ? 0 : PHY_LED_POLARITY(index));
++}
++
+ static struct phy_driver gpy_drivers[] = {
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
+@@ -844,6 +1017,11 @@ static struct phy_driver gpy_drivers[] =
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_get = gpy_led_hw_control_get,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_polarity_set = gpy_led_polarity_set,
+       },
+       {
+               .phy_id         = PHY_ID_GPY115B,
+@@ -862,6 +1040,11 @@ static struct phy_driver gpy_drivers[] =
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy115_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_get = gpy_led_hw_control_get,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_polarity_set = gpy_led_polarity_set,
+       },
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
+@@ -879,6 +1062,11 @@ static struct phy_driver gpy_drivers[] =
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy115_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_get = gpy_led_hw_control_get,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_polarity_set = gpy_led_polarity_set,
+       },
+       {
+               .phy_id         = PHY_ID_GPY211B,
+@@ -897,6 +1085,11 @@ static struct phy_driver gpy_drivers[] =
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_get = gpy_led_hw_control_get,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_polarity_set = gpy_led_polarity_set,
+       },
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
+@@ -914,6 +1107,11 @@ static struct phy_driver gpy_drivers[] =
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_get = gpy_led_hw_control_get,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_polarity_set = gpy_led_polarity_set,
+       },
+       {
+               .phy_id         = PHY_ID_GPY212B,
+@@ -932,6 +1130,11 @@ static struct phy_driver gpy_drivers[] =
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_get = gpy_led_hw_control_get,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_polarity_set = gpy_led_polarity_set,
+       },
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
+@@ -949,6 +1152,11 @@ static struct phy_driver gpy_drivers[] =
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_get = gpy_led_hw_control_get,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_polarity_set = gpy_led_polarity_set,
+       },
+       {
+               .phy_id         = PHY_ID_GPY215B,
+@@ -967,6 +1175,11 @@ static struct phy_driver gpy_drivers[] =
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_get = gpy_led_hw_control_get,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_polarity_set = gpy_led_polarity_set,
+       },
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
+@@ -984,6 +1197,11 @@ static struct phy_driver gpy_drivers[] =
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_get = gpy_led_hw_control_get,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_polarity_set = gpy_led_polarity_set,
+       },
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
diff --git a/target/linux/generic/backport-6.6/844-v6.13-net-phy-mxl-gpy-add-missing-support-for-TRIGGER_NETD.patch b/target/linux/generic/backport-6.6/844-v6.13-net-phy-mxl-gpy-add-missing-support-for-TRIGGER_NETD.patch
new file mode 100644 (file)
index 0000000..067c62d
--- /dev/null
@@ -0,0 +1,28 @@
+From f95b4725e796b12e5f347a0d161e1d3843142aa8 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Fri, 4 Oct 2024 16:56:35 +0100
+Subject: [PATCH] net: phy: mxl-gpy: add missing support for
+ TRIGGER_NETDEV_LINK_10
+
+The PHY also support 10MBit/s links as well as the corresponding link
+indication trigger to be offloaded. Add TRIGGER_NETDEV_LINK_10 to the
+supported triggers.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/cc5da0a989af8b0d49d823656d88053c4de2ab98.1728057367.git.daniel@makrotopia.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/mxl-gpy.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/phy/mxl-gpy.c
++++ b/drivers/net/phy/mxl-gpy.c
+@@ -876,6 +876,7 @@ static int gpy_led_brightness_set(struct
+ }
+ static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
++                                               BIT(TRIGGER_NETDEV_LINK_10) |
+                                                BIT(TRIGGER_NETDEV_LINK_100) |
+                                                BIT(TRIGGER_NETDEV_LINK_1000) |
+                                                BIT(TRIGGER_NETDEV_LINK_2500) |
diff --git a/target/linux/generic/backport-6.6/845-v6.13-net-phy-mxl-gpy-correctly-describe-LED-polarity.patch b/target/linux/generic/backport-6.6/845-v6.13-net-phy-mxl-gpy-correctly-describe-LED-polarity.patch
new file mode 100644 (file)
index 0000000..5b88548
--- /dev/null
@@ -0,0 +1,58 @@
+From eb89c79c1b8f17fc1611540768678e60df89ac42 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 10 Oct 2024 13:55:17 +0100
+Subject: [PATCH 3/4] net: phy: mxl-gpy: correctly describe LED polarity
+
+According the datasheet covering the LED (0x1b) register:
+0B Active High LEDx pin driven high when activated
+1B Active Low LEDx pin driven low when activated
+
+Make use of the now available 'active-high' property and correctly
+reflect the polarity setting which was previously inverted.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/180ccafa837f09908b852a8a874a3808c5ecd2d0.1728558223.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/mxl-gpy.c | 16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/mxl-gpy.c
++++ b/drivers/net/phy/mxl-gpy.c
+@@ -981,7 +981,7 @@ static int gpy_led_hw_control_set(struct
+ static int gpy_led_polarity_set(struct phy_device *phydev, int index,
+                               unsigned long modes)
+ {
+-      bool active_low = false;
++      bool force_active_low = false, force_active_high = false;
+       u32 mode;
+       if (index >= GPY_MAX_LEDS)
+@@ -990,15 +990,23 @@ static int gpy_led_polarity_set(struct p
+       for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
+               switch (mode) {
+               case PHY_LED_ACTIVE_LOW:
+-                      active_low = true;
++                      force_active_low = true;
++                      break;
++              case PHY_LED_ACTIVE_HIGH:
++                      force_active_high = true;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+-      return phy_modify(phydev, PHY_LED, PHY_LED_POLARITY(index),
+-                        active_low ? 0 : PHY_LED_POLARITY(index));
++      if (force_active_low)
++              return phy_set_bits(phydev, PHY_LED, PHY_LED_POLARITY(index));
++
++      if (force_active_high)
++              return phy_clear_bits(phydev, PHY_LED, PHY_LED_POLARITY(index));
++
++      unreachable();
+ }
+ static struct phy_driver gpy_drivers[] = {
diff --git a/target/linux/generic/backport-6.6/846-v6.13-net-phy-intel-xway-add-support-for-PHY-LEDs.patch b/target/linux/generic/backport-6.6/846-v6.13-net-phy-intel-xway-add-support-for-PHY-LEDs.patch
new file mode 100644 (file)
index 0000000..c57b577
--- /dev/null
@@ -0,0 +1,379 @@
+From 1758af47b98c17da464cb45f476875150955dd48 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 10 Oct 2024 13:55:29 +0100
+Subject: [PATCH 4/4] net: phy: intel-xway: add support for PHY LEDs
+
+The intel-xway PHY driver predates the PHY LED framework and currently
+initializes all LED pins to equal default values.
+
+Add PHY LED functions to the drivers and don't set default values if
+LEDs are defined in device tree.
+
+According the datasheets 3 LEDs are supported on all Intel XWAY PHYs.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/81f4717ab9acf38f3239727a4540ae96fd01109b.1728558223.git.daniel@makrotopia.org
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/intel-xway.c | 253 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 244 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/phy/intel-xway.c
++++ b/drivers/net/phy/intel-xway.c
+@@ -151,6 +151,13 @@
+ #define XWAY_MMD_LED3H                        0x01E8
+ #define XWAY_MMD_LED3L                        0x01E9
++#define XWAY_GPHY_MAX_LEDS            3
++#define XWAY_GPHY_LED_INV(idx)                BIT(12 + (idx))
++#define XWAY_GPHY_LED_EN(idx)         BIT(8 + (idx))
++#define XWAY_GPHY_LED_DA(idx)         BIT(idx)
++#define XWAY_MMD_LEDxH(idx)           (XWAY_MMD_LED0H + 2 * (idx))
++#define XWAY_MMD_LEDxL(idx)           (XWAY_MMD_LED0L + 2 * (idx))
++
+ #define PHY_ID_PHY11G_1_3             0x030260D1
+ #define PHY_ID_PHY22F_1_3             0x030260E1
+ #define PHY_ID_PHY11G_1_4             0xD565A400
+@@ -229,20 +236,12 @@ static int xway_gphy_rgmii_init(struct p
+                         XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
+ }
+-static int xway_gphy_config_init(struct phy_device *phydev)
++static int xway_gphy_init_leds(struct phy_device *phydev)
+ {
+       int err;
+       u32 ledxh;
+       u32 ledxl;
+-      /* Mask all interrupts */
+-      err = phy_write(phydev, XWAY_MDIO_IMASK, 0);
+-      if (err)
+-              return err;
+-
+-      /* Clear all pending interrupts */
+-      phy_read(phydev, XWAY_MDIO_ISTAT);
+-
+       /* Ensure that integrated led function is enabled for all leds */
+       err = phy_write(phydev, XWAY_MDIO_LED,
+                       XWAY_MDIO_LED_LED0_EN |
+@@ -276,6 +275,26 @@ static int xway_gphy_config_init(struct
+       phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh);
+       phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl);
++      return 0;
++}
++
++static int xway_gphy_config_init(struct phy_device *phydev)
++{
++      struct device_node *np = phydev->mdio.dev.of_node;
++      int err;
++
++      /* Mask all interrupts */
++      err = phy_write(phydev, XWAY_MDIO_IMASK, 0);
++      if (err)
++              return err;
++
++      /* Use default LED configuration if 'leds' node isn't defined */
++      if (!of_get_child_by_name(np, "leds"))
++              xway_gphy_init_leds(phydev);
++
++      /* Clear all pending interrupts */
++      phy_read(phydev, XWAY_MDIO_ISTAT);
++
+       err = xway_gphy_rgmii_init(phydev);
+       if (err)
+               return err;
+@@ -347,6 +366,172 @@ static irqreturn_t xway_gphy_handle_inte
+       return IRQ_HANDLED;
+ }
++static int xway_gphy_led_brightness_set(struct phy_device *phydev,
++                                      u8 index, enum led_brightness value)
++{
++      int ret;
++
++      if (index >= XWAY_GPHY_MAX_LEDS)
++              return -EINVAL;
++
++      /* clear EN and set manual LED state */
++      ret = phy_modify(phydev, XWAY_MDIO_LED,
++                       ((value == LED_OFF) ? XWAY_GPHY_LED_EN(index) : 0) |
++                       XWAY_GPHY_LED_DA(index),
++                       (value == LED_OFF) ? 0 : XWAY_GPHY_LED_DA(index));
++      if (ret)
++              return ret;
++
++      /* clear HW LED setup */
++      if (value == LED_OFF) {
++              ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), 0);
++              if (ret)
++                      return ret;
++
++              return phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), 0);
++      } else {
++              return 0;
++      }
++}
++
++static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
++                                               BIT(TRIGGER_NETDEV_LINK_10) |
++                                               BIT(TRIGGER_NETDEV_LINK_100) |
++                                               BIT(TRIGGER_NETDEV_LINK_1000) |
++                                               BIT(TRIGGER_NETDEV_RX) |
++                                               BIT(TRIGGER_NETDEV_TX));
++
++static int xway_gphy_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                       unsigned long rules)
++{
++      if (index >= XWAY_GPHY_MAX_LEDS)
++              return -EINVAL;
++
++      /* activity triggers are not possible without combination with a link
++       * trigger.
++       */
++      if (rules & (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX)) &&
++          !(rules & (BIT(TRIGGER_NETDEV_LINK) |
++                     BIT(TRIGGER_NETDEV_LINK_10) |
++                     BIT(TRIGGER_NETDEV_LINK_100) |
++                     BIT(TRIGGER_NETDEV_LINK_1000))))
++              return -EOPNOTSUPP;
++
++      /* All other combinations of the supported triggers are allowed */
++      if (rules & ~supported_triggers)
++              return -EOPNOTSUPP;
++
++      return 0;
++}
++
++static int xway_gphy_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                      unsigned long *rules)
++{
++      int lval, hval;
++
++      if (index >= XWAY_GPHY_MAX_LEDS)
++              return -EINVAL;
++
++      hval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index));
++      if (hval < 0)
++              return hval;
++
++      lval = phy_read_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index));
++      if (lval < 0)
++              return lval;
++
++      if (hval & XWAY_MMD_LEDxH_CON_LINK10)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_10);
++
++      if (hval & XWAY_MMD_LEDxH_CON_LINK100)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_100);
++
++      if (hval & XWAY_MMD_LEDxH_CON_LINK1000)
++              *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
++
++      if ((hval & XWAY_MMD_LEDxH_CON_LINK10) &&
++          (hval & XWAY_MMD_LEDxH_CON_LINK100) &&
++          (hval & XWAY_MMD_LEDxH_CON_LINK1000))
++              *rules |= BIT(TRIGGER_NETDEV_LINK);
++
++      if (lval & XWAY_MMD_LEDxL_PULSE_TXACT)
++              *rules |= BIT(TRIGGER_NETDEV_TX);
++
++      if (lval & XWAY_MMD_LEDxL_PULSE_RXACT)
++              *rules |= BIT(TRIGGER_NETDEV_RX);
++
++      return 0;
++}
++
++static int xway_gphy_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                      unsigned long rules)
++{
++      u16 hval = 0, lval = 0;
++      int ret;
++
++      if (index >= XWAY_GPHY_MAX_LEDS)
++              return -EINVAL;
++
++      if (rules & BIT(TRIGGER_NETDEV_LINK) ||
++          rules & BIT(TRIGGER_NETDEV_LINK_10))
++              hval |= XWAY_MMD_LEDxH_CON_LINK10;
++
++      if (rules & BIT(TRIGGER_NETDEV_LINK) ||
++          rules & BIT(TRIGGER_NETDEV_LINK_100))
++              hval |= XWAY_MMD_LEDxH_CON_LINK100;
++
++      if (rules & BIT(TRIGGER_NETDEV_LINK) ||
++          rules & BIT(TRIGGER_NETDEV_LINK_1000))
++              hval |= XWAY_MMD_LEDxH_CON_LINK1000;
++
++      if (rules & BIT(TRIGGER_NETDEV_TX))
++              lval |= XWAY_MMD_LEDxL_PULSE_TXACT;
++
++      if (rules & BIT(TRIGGER_NETDEV_RX))
++              lval |= XWAY_MMD_LEDxL_PULSE_RXACT;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxH(index), hval);
++      if (ret)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDxL(index), lval);
++      if (ret)
++              return ret;
++
++      return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_EN(index));
++}
++
++static int xway_gphy_led_polarity_set(struct phy_device *phydev, int index,
++                                    unsigned long modes)
++{
++      bool force_active_low = false, force_active_high = false;
++      u32 mode;
++
++      if (index >= XWAY_GPHY_MAX_LEDS)
++              return -EINVAL;
++
++      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++              switch (mode) {
++              case PHY_LED_ACTIVE_LOW:
++                      force_active_low = true;
++                      break;
++              case PHY_LED_ACTIVE_HIGH:
++                      force_active_high = true;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      if (force_active_low)
++              return phy_set_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index));
++
++      if (force_active_high)
++              return phy_clear_bits(phydev, XWAY_MDIO_LED, XWAY_GPHY_LED_INV(index));
++
++      unreachable();
++}
++
+ static struct phy_driver xway_gphy[] = {
+       {
+               .phy_id         = PHY_ID_PHY11G_1_3,
+@@ -359,6 +544,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       }, {
+               .phy_id         = PHY_ID_PHY22F_1_3,
+               .phy_id_mask    = 0xffffffff,
+@@ -370,6 +560,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       }, {
+               .phy_id         = PHY_ID_PHY11G_1_4,
+               .phy_id_mask    = 0xffffffff,
+@@ -381,6 +576,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       }, {
+               .phy_id         = PHY_ID_PHY22F_1_4,
+               .phy_id_mask    = 0xffffffff,
+@@ -392,6 +592,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       }, {
+               .phy_id         = PHY_ID_PHY11G_1_5,
+               .phy_id_mask    = 0xffffffff,
+@@ -402,6 +607,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       }, {
+               .phy_id         = PHY_ID_PHY22F_1_5,
+               .phy_id_mask    = 0xffffffff,
+@@ -412,6 +622,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       }, {
+               .phy_id         = PHY_ID_PHY11G_VR9_1_1,
+               .phy_id_mask    = 0xffffffff,
+@@ -422,6 +637,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       }, {
+               .phy_id         = PHY_ID_PHY22F_VR9_1_1,
+               .phy_id_mask    = 0xffffffff,
+@@ -432,6 +652,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       }, {
+               .phy_id         = PHY_ID_PHY11G_VR9_1_2,
+               .phy_id_mask    = 0xffffffff,
+@@ -442,6 +667,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       }, {
+               .phy_id         = PHY_ID_PHY22F_VR9_1_2,
+               .phy_id_mask    = 0xffffffff,
+@@ -452,6 +682,11 @@ static struct phy_driver xway_gphy[] = {
+               .config_intr    = xway_gphy_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
++              .led_brightness_set = xway_gphy_led_brightness_set,
++              .led_hw_is_supported = xway_gphy_led_hw_is_supported,
++              .led_hw_control_get = xway_gphy_led_hw_control_get,
++              .led_hw_control_set = xway_gphy_led_hw_control_set,
++              .led_polarity_set = xway_gphy_led_polarity_set,
+       },
+ };
+ module_phy_driver(xway_gphy);
diff --git a/target/linux/generic/backport-6.6/850-v6.8-bus-mhi-host-Add-a-separate-timeout-parameter-for-wa.patch b/target/linux/generic/backport-6.6/850-v6.8-bus-mhi-host-Add-a-separate-timeout-parameter-for-wa.patch
new file mode 100644 (file)
index 0000000..94d9bcd
--- /dev/null
@@ -0,0 +1,175 @@
+From 6ab3d50b106c9aea123a80551a6c9deace83b914 Mon Sep 17 00:00:00 2001
+From: Qiang Yu <quic_qianyu@quicinc.com>
+Date: Tue, 7 Nov 2023 16:14:49 +0800
+Subject: [PATCH] bus: mhi: host: Add a separate timeout parameter for waiting
+ ready
+
+Some devices(eg. SDX75) take longer than expected (default, 8 seconds) to
+set ready after reboot. Hence add optional ready timeout parameter and pass
+the appropriate timeout value to mhi_poll_reg_field() to wait enough for
+device ready as part of power up sequence.
+
+Signed-off-by: Qiang Yu <quic_qianyu@quicinc.com>
+Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Link: https://lore.kernel.org/r/1699344890-87076-2-git-send-email-quic_qianyu@quicinc.com
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+---
+ drivers/bus/mhi/host/init.c     |  1 +
+ drivers/bus/mhi/host/internal.h |  2 +-
+ drivers/bus/mhi/host/main.c     |  5 +++--
+ drivers/bus/mhi/host/pm.c       | 24 +++++++++++++++++-------
+ include/linux/mhi.h             |  4 ++++
+ 5 files changed, 26 insertions(+), 10 deletions(-)
+
+--- a/drivers/bus/mhi/host/init.c
++++ b/drivers/bus/mhi/host/init.c
+@@ -882,6 +882,7 @@ static int parse_config(struct mhi_contr
+       if (!mhi_cntrl->timeout_ms)
+               mhi_cntrl->timeout_ms = MHI_TIMEOUT_MS;
++      mhi_cntrl->ready_timeout_ms = config->ready_timeout_ms;
+       mhi_cntrl->bounce_buf = config->use_bounce_buf;
+       mhi_cntrl->buffer_len = config->buf_len;
+       if (!mhi_cntrl->buffer_len)
+--- a/drivers/bus/mhi/host/internal.h
++++ b/drivers/bus/mhi/host/internal.h
+@@ -324,7 +324,7 @@ int __must_check mhi_read_reg_field(stru
+                                   u32 *out);
+ int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
+                                   void __iomem *base, u32 offset, u32 mask,
+-                                  u32 val, u32 delayus);
++                                  u32 val, u32 delayus, u32 timeout_ms);
+ void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
+                  u32 offset, u32 val);
+ int __must_check mhi_write_reg_field(struct mhi_controller *mhi_cntrl,
+--- a/drivers/bus/mhi/host/main.c
++++ b/drivers/bus/mhi/host/main.c
+@@ -40,10 +40,11 @@ int __must_check mhi_read_reg_field(stru
+ int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
+                                   void __iomem *base, u32 offset,
+-                                  u32 mask, u32 val, u32 delayus)
++                                  u32 mask, u32 val, u32 delayus,
++                                  u32 timeout_ms)
+ {
+       int ret;
+-      u32 out, retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
++      u32 out, retry = (timeout_ms * 1000) / delayus;
+       while (retry--) {
+               ret = mhi_read_reg_field(mhi_cntrl, base, offset, mask, &out);
+--- a/drivers/bus/mhi/host/pm.c
++++ b/drivers/bus/mhi/host/pm.c
+@@ -171,6 +171,7 @@ int mhi_ready_state_transition(struct mh
+       enum mhi_pm_state cur_state;
+       struct device *dev = &mhi_cntrl->mhi_dev->dev;
+       u32 interval_us = 25000; /* poll register field every 25 milliseconds */
++      u32 timeout_ms;
+       int ret, i;
+       /* Check if device entered error state */
+@@ -181,14 +182,18 @@ int mhi_ready_state_transition(struct mh
+       /* Wait for RESET to be cleared and READY bit to be set by the device */
+       ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
+-                               MHICTRL_RESET_MASK, 0, interval_us);
++                               MHICTRL_RESET_MASK, 0, interval_us,
++                               mhi_cntrl->timeout_ms);
+       if (ret) {
+               dev_err(dev, "Device failed to clear MHI Reset\n");
+               return ret;
+       }
++      timeout_ms = mhi_cntrl->ready_timeout_ms ?
++              mhi_cntrl->ready_timeout_ms : mhi_cntrl->timeout_ms;
+       ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS,
+-                               MHISTATUS_READY_MASK, 1, interval_us);
++                               MHISTATUS_READY_MASK, 1, interval_us,
++                               timeout_ms);
+       if (ret) {
+               dev_err(dev, "Device failed to enter MHI Ready\n");
+               return ret;
+@@ -487,7 +492,7 @@ static void mhi_pm_disable_transition(st
+               /* Wait for the reset bit to be cleared by the device */
+               ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
+-                               MHICTRL_RESET_MASK, 0, 25000);
++                               MHICTRL_RESET_MASK, 0, 25000, mhi_cntrl->timeout_ms);
+               if (ret)
+                       dev_err(dev, "Device failed to clear MHI Reset\n");
+@@ -500,8 +505,8 @@ static void mhi_pm_disable_transition(st
+               if (!MHI_IN_PBL(mhi_get_exec_env(mhi_cntrl))) {
+                       /* wait for ready to be set */
+                       ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs,
+-                                               MHISTATUS,
+-                                               MHISTATUS_READY_MASK, 1, 25000);
++                                               MHISTATUS, MHISTATUS_READY_MASK,
++                                               1, 25000, mhi_cntrl->timeout_ms);
+                       if (ret)
+                               dev_err(dev, "Device failed to enter READY state\n");
+               }
+@@ -1125,7 +1130,8 @@ int mhi_async_power_up(struct mhi_contro
+       if (state == MHI_STATE_SYS_ERR) {
+               mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET);
+               ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
+-                               MHICTRL_RESET_MASK, 0, interval_us);
++                               MHICTRL_RESET_MASK, 0, interval_us,
++                               mhi_cntrl->timeout_ms);
+               if (ret) {
+                       dev_info(dev, "Failed to reset MHI due to syserr state\n");
+                       goto error_exit;
+@@ -1216,14 +1222,18 @@ EXPORT_SYMBOL_GPL(mhi_power_down);
+ int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
+ {
+       int ret = mhi_async_power_up(mhi_cntrl);
++      u32 timeout_ms;
+       if (ret)
+               return ret;
++      /* Some devices need more time to set ready during power up */
++      timeout_ms = mhi_cntrl->ready_timeout_ms ?
++              mhi_cntrl->ready_timeout_ms : mhi_cntrl->timeout_ms;
+       wait_event_timeout(mhi_cntrl->state_event,
+                          MHI_IN_MISSION_MODE(mhi_cntrl->ee) ||
+                          MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
+-                         msecs_to_jiffies(mhi_cntrl->timeout_ms));
++                         msecs_to_jiffies(timeout_ms));
+       ret = (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) ? 0 : -ETIMEDOUT;
+       if (ret)
+--- a/include/linux/mhi.h
++++ b/include/linux/mhi.h
+@@ -266,6 +266,7 @@ struct mhi_event_config {
+  * struct mhi_controller_config - Root MHI controller configuration
+  * @max_channels: Maximum number of channels supported
+  * @timeout_ms: Timeout value for operations. 0 means use default
++ * @ready_timeout_ms: Timeout value for waiting device to be ready (optional)
+  * @buf_len: Size of automatically allocated buffers. 0 means use default
+  * @num_channels: Number of channels defined in @ch_cfg
+  * @ch_cfg: Array of defined channels
+@@ -277,6 +278,7 @@ struct mhi_event_config {
+ struct mhi_controller_config {
+       u32 max_channels;
+       u32 timeout_ms;
++      u32 ready_timeout_ms;
+       u32 buf_len;
+       u32 num_channels;
+       const struct mhi_channel_config *ch_cfg;
+@@ -330,6 +332,7 @@ struct mhi_controller_config {
+  * @pm_mutex: Mutex for suspend/resume operation
+  * @pm_lock: Lock for protecting MHI power management state
+  * @timeout_ms: Timeout in ms for state transitions
++ * @ready_timeout_ms: Timeout in ms for waiting device to be ready (optional)
+  * @pm_state: MHI power management state
+  * @db_access: DB access states
+  * @ee: MHI device execution environment
+@@ -419,6 +422,7 @@ struct mhi_controller {
+       struct mutex pm_mutex;
+       rwlock_t pm_lock;
+       u32 timeout_ms;
++      u32 ready_timeout_ms;
+       u32 pm_state;
+       u32 db_access;
+       enum mhi_ee_type ee;
diff --git a/target/linux/generic/backport-6.6/851-v6.8-bus-mhi-host-pci_generic-Add-SDX75-based-modem-suppo.patch b/target/linux/generic/backport-6.6/851-v6.8-bus-mhi-host-pci_generic-Add-SDX75-based-modem-suppo.patch
new file mode 100644 (file)
index 0000000..6a09e61
--- /dev/null
@@ -0,0 +1,62 @@
+From 4dc9c850a974ba7db2091ce73bcffe631aafe144 Mon Sep 17 00:00:00 2001
+From: Qiang Yu <quic_qianyu@quicinc.com>
+Date: Tue, 7 Nov 2023 16:14:50 +0800
+Subject: [PATCH 1/2] bus: mhi: host: pci_generic: Add SDX75 based modem
+ support
+
+Add generic info for SDX75 based modems. SDX75 takes longer to set ready
+during power up. Hence use separate configuration.
+
+Signed-off-by: Qiang Yu <quic_qianyu@quicinc.com>
+Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Link: https://lore.kernel.org/r/1699344890-87076-3-git-send-email-quic_qianyu@quicinc.com
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+---
+ drivers/bus/mhi/host/pci_generic.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+--- a/drivers/bus/mhi/host/pci_generic.c
++++ b/drivers/bus/mhi/host/pci_generic.c
+@@ -269,6 +269,16 @@ static struct mhi_event_config modem_qco
+       MHI_EVENT_CONFIG_HW_DATA(5, 2048, 101)
+ };
++static const struct mhi_controller_config modem_qcom_v2_mhiv_config = {
++      .max_channels = 128,
++      .timeout_ms = 8000,
++      .ready_timeout_ms = 50000,
++      .num_channels = ARRAY_SIZE(modem_qcom_v1_mhi_channels),
++      .ch_cfg = modem_qcom_v1_mhi_channels,
++      .num_events = ARRAY_SIZE(modem_qcom_v1_mhi_events),
++      .event_cfg = modem_qcom_v1_mhi_events,
++};
++
+ static const struct mhi_controller_config modem_qcom_v1_mhiv_config = {
+       .max_channels = 128,
+       .timeout_ms = 8000,
+@@ -278,6 +288,16 @@ static const struct mhi_controller_confi
+       .event_cfg = modem_qcom_v1_mhi_events,
+ };
++static const struct mhi_pci_dev_info mhi_qcom_sdx75_info = {
++      .name = "qcom-sdx75m",
++      .fw = "qcom/sdx75m/xbl.elf",
++      .edl = "qcom/sdx75m/edl.mbn",
++      .config = &modem_qcom_v2_mhiv_config,
++      .bar_num = MHI_PCI_DEFAULT_BAR_NUM,
++      .dma_data_width = 32,
++      .sideband_wake = false,
++};
++
+ static const struct mhi_pci_dev_info mhi_qcom_sdx65_info = {
+       .name = "qcom-sdx65m",
+       .fw = "qcom/sdx65m/xbl.elf",
+@@ -609,6 +629,8 @@ static const struct pci_device_id mhi_pc
+               .driver_data = (kernel_ulong_t) &mhi_telit_fe990a_info },
+       { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308),
+               .driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info },
++      { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0309),
++              .driver_data = (kernel_ulong_t) &mhi_qcom_sdx75_info },
+       { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1001), /* EM120R-GL (sdx24) */
+               .driver_data = (kernel_ulong_t) &mhi_quectel_em1xx_info },
+       { PCI_DEVICE(PCI_VENDOR_ID_QUECTEL, 0x1002), /* EM160R-GL (sdx24) */
diff --git a/target/linux/generic/backport-6.6/852-stable-bus-mhi-host-pci_generic-constify-modem_telit_fn980_.patch b/target/linux/generic/backport-6.6/852-stable-bus-mhi-host-pci_generic-constify-modem_telit_fn980_.patch
new file mode 100644 (file)
index 0000000..e6df789
--- /dev/null
@@ -0,0 +1,28 @@
+From 2f5e59d70566902d7b4e13c6af3f042f5d28b78b Mon Sep 17 00:00:00 2001
+From: Jeff Johnson <quic_jjohnson@quicinc.com>
+Date: Thu, 22 Feb 2024 18:00:23 -0800
+Subject: [PATCH 2/2] bus: mhi: host: pci_generic: constify
+ modem_telit_fn980_hw_v1_config
+
+MHI expects the controller configs to be const, and all of the other ones
+in this file already are, so constify modem_telit_fn980_hw_v1_config.
+
+Signed-off-by: Jeff Johnson <quic_jjohnson@quicinc.com>
+Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+Link: https://lore.kernel.org/r/20240222-mhi-const-bus-mhi-host-pci_generic-v1-1-d4c9b0b0a7a5@quicinc.com
+Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+---
+ drivers/bus/mhi/host/pci_generic.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/bus/mhi/host/pci_generic.c
++++ b/drivers/bus/mhi/host/pci_generic.c
+@@ -538,7 +538,7 @@ static struct mhi_event_config mhi_telit
+       MHI_EVENT_CONFIG_HW_DATA(2, 2048, 101)
+ };
+-static struct mhi_controller_config modem_telit_fn980_hw_v1_config = {
++static const struct mhi_controller_config modem_telit_fn980_hw_v1_config = {
+       .max_channels = 128,
+       .timeout_ms = 20000,
+       .num_channels = ARRAY_SIZE(mhi_telit_fn980_hw_v1_channels),
diff --git a/target/linux/generic/backport-6.6/853-v6.10-bus-mhi-host-Add-mhi_power_down_keep_dev-API-to-supp.patch b/target/linux/generic/backport-6.6/853-v6.10-bus-mhi-host-Add-mhi_power_down_keep_dev-API-to-supp.patch
new file mode 100644 (file)
index 0000000..826b39b
--- /dev/null
@@ -0,0 +1,140 @@
+--- a/drivers/bus/mhi/host/init.c
++++ b/drivers/bus/mhi/host/init.c
+@@ -43,6 +43,7 @@ const char * const dev_state_tran_str[DE
+       [DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
+       [DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
+       [DEV_ST_TRANSITION_DISABLE] = "DISABLE",
++      [DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE] = "DISABLE (DESTROY DEVICE)",
+ };
+ const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
+--- a/drivers/bus/mhi/host/internal.h
++++ b/drivers/bus/mhi/host/internal.h
+@@ -69,6 +69,7 @@ enum dev_st_transition {
+       DEV_ST_TRANSITION_FP,
+       DEV_ST_TRANSITION_SYS_ERR,
+       DEV_ST_TRANSITION_DISABLE,
++      DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
+       DEV_ST_TRANSITION_MAX,
+ };
+--- a/drivers/bus/mhi/host/pm.c
++++ b/drivers/bus/mhi/host/pm.c
+@@ -466,7 +466,8 @@ error_mission_mode:
+ }
+ /* Handle shutdown transitions */
+-static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl)
++static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
++                                    bool destroy_device)
+ {
+       enum mhi_pm_state cur_state;
+       struct mhi_event *mhi_event;
+@@ -528,8 +529,16 @@ skip_mhi_reset:
+       dev_dbg(dev, "Waiting for all pending threads to complete\n");
+       wake_up_all(&mhi_cntrl->state_event);
+-      dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
+-      device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
++      /*
++       * Only destroy the 'struct device' for channels if indicated by the
++       * 'destroy_device' flag. Because, during system suspend or hibernation
++       * state, there is no need to destroy the 'struct device' as the endpoint
++       * device would still be physically attached to the machine.
++       */
++      if (destroy_device) {
++              dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
++              device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device);
++      }
+       mutex_lock(&mhi_cntrl->pm_mutex);
+@@ -820,7 +829,10 @@ void mhi_pm_st_worker(struct work_struct
+                       mhi_pm_sys_error_transition(mhi_cntrl);
+                       break;
+               case DEV_ST_TRANSITION_DISABLE:
+-                      mhi_pm_disable_transition(mhi_cntrl);
++                      mhi_pm_disable_transition(mhi_cntrl, false);
++                      break;
++              case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE:
++                      mhi_pm_disable_transition(mhi_cntrl, true);
+                       break;
+               default:
+                       break;
+@@ -1174,7 +1186,8 @@ error_exit:
+ }
+ EXPORT_SYMBOL_GPL(mhi_async_power_up);
+-void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
++static void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful,
++                           bool destroy_device)
+ {
+       enum mhi_pm_state cur_state, transition_state;
+       struct device *dev = &mhi_cntrl->mhi_dev->dev;
+@@ -1210,15 +1223,32 @@ void mhi_power_down(struct mhi_controlle
+       write_unlock_irq(&mhi_cntrl->pm_lock);
+       mutex_unlock(&mhi_cntrl->pm_mutex);
+-      mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE);
++      if (destroy_device)
++              mhi_queue_state_transition(mhi_cntrl,
++                                         DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE);
++      else
++              mhi_queue_state_transition(mhi_cntrl,
++                                         DEV_ST_TRANSITION_DISABLE);
+       /* Wait for shutdown to complete */
+       flush_work(&mhi_cntrl->st_worker);
+       disable_irq(mhi_cntrl->irq[0]);
+ }
++
++void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
++{
++      __mhi_power_down(mhi_cntrl, graceful, true);
++}
+ EXPORT_SYMBOL_GPL(mhi_power_down);
++void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl,
++                             bool graceful)
++{
++      __mhi_power_down(mhi_cntrl, graceful, false);
++}
++EXPORT_SYMBOL_GPL(mhi_power_down_keep_dev);
++
+ int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
+ {
+       int ret = mhi_async_power_up(mhi_cntrl);
+--- a/include/linux/mhi.h
++++ b/include/linux/mhi.h
+@@ -649,13 +649,29 @@ int mhi_async_power_up(struct mhi_contro
+ int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
+ /**
+- * mhi_power_down - Start MHI power down sequence
++ * mhi_power_down - Power down the MHI device and also destroy the
++ *                  'struct device' for the channels associated with it.
++ *                  See also mhi_power_down_keep_dev() which is a variant
++ *                  of this API that keeps the 'struct device' for channels
++ *                  (useful during suspend/hibernation).
+  * @mhi_cntrl: MHI controller
+  * @graceful: Link is still accessible, so do a graceful shutdown process
+  */
+ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
+ /**
++ * mhi_power_down_keep_dev - Power down the MHI device but keep the 'struct
++ *                           device' for the channels associated with it.
++ *                           This is a variant of 'mhi_power_down()' and
++ *                           useful in scenarios such as suspend/hibernation
++ *                           where destroying of the 'struct device' is not
++ *                           needed.
++ * @mhi_cntrl: MHI controller
++ * @graceful: Link is still accessible, so do a graceful shutdown process
++ */
++void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, bool graceful);
++
++/**
+  * mhi_unprepare_after_power_down - Free any allocated memory after power down
+  * @mhi_cntrl: MHI controller
+  */
diff --git a/target/linux/generic/backport-6.6/860-v6.7-leds-add-ktd202x-driver.patch b/target/linux/generic/backport-6.6/860-v6.7-leds-add-ktd202x-driver.patch
new file mode 100644 (file)
index 0000000..fb767c5
--- /dev/null
@@ -0,0 +1,682 @@
+From 0ebdb7210943eb345992bea9892adbd15a206193 Mon Sep 17 00:00:00 2001
+From: André Apitzsch <git@apitzsch.eu>
+Date: Mon, 2 Oct 2023 18:48:28 +0200
+Subject: leds: Add ktd202x driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This commit adds support for Kinetic KTD2026/7 RGB/White LED driver.
+
+Signed-off-by: André Apitzsch <git@apitzsch.eu>
+Link: https://lore.kernel.org/r/20231002-ktd202x-v6-2-26be8eefeb88@apitzsch.eu
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/rgb/Kconfig        |  13 +
+ drivers/leds/rgb/Makefile       |   1 +
+ drivers/leds/rgb/leds-ktd202x.c | 625 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 639 insertions(+)
+ create mode 100644 drivers/leds/rgb/leds-ktd202x.c
+
+(limited to 'drivers/leds/rgb')
+
+--- a/drivers/leds/rgb/Kconfig
++++ b/drivers/leds/rgb/Kconfig
+@@ -14,6 +14,19 @@ config LEDS_GROUP_MULTICOLOR
+         To compile this driver as a module, choose M here: the module
+         will be called leds-group-multicolor.
++config LEDS_KTD202X
++      tristate "LED support for KTD202x Chips"
++      depends on I2C
++      depends on OF
++      select REGMAP_I2C
++      help
++        This option enables support for the Kinetic KTD2026/KTD2027
++        RGB/White LED driver found in different BQ mobile phones.
++        It is a 3 or 4 channel LED driver programmed via an I2C interface.
++
++        To compile this driver as a module, choose M here: the module
++        will be called leds-ktd202x.
++
+ config LEDS_PWM_MULTICOLOR
+       tristate "PWM driven multi-color LED Support"
+       depends on PWM
+--- a/drivers/leds/rgb/Makefile
++++ b/drivers/leds/rgb/Makefile
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-$(CONFIG_LEDS_GROUP_MULTICOLOR)   += leds-group-multicolor.o
++obj-$(CONFIG_LEDS_KTD202X)            += leds-ktd202x.o
+ obj-$(CONFIG_LEDS_PWM_MULTICOLOR)     += leds-pwm-multicolor.o
+ obj-$(CONFIG_LEDS_QCOM_LPG)           += leds-qcom-lpg.o
+ obj-$(CONFIG_LEDS_MT6370_RGB)         += leds-mt6370-rgb.o
+--- /dev/null
++++ b/drivers/leds/rgb/leds-ktd202x.c
+@@ -0,0 +1,625 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Kinetic KTD2026/7 RGB/White LED driver with I2C interface
++ *
++ * Copyright 2023 André Apitzsch <git@apitzsch.eu>
++ *
++ * Datasheet: https://www.kinet-ic.com/uploads/KTD2026-7-04h.pdf
++ */
++
++#include <linux/i2c.h>
++#include <linux/led-class-multicolor.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/regmap.h>
++#include <linux/regulator/consumer.h>
++
++#define KTD2026_NUM_LEDS 3
++#define KTD2027_NUM_LEDS 4
++#define KTD202X_MAX_LEDS 4
++
++/* Register bank */
++#define KTD202X_REG_RESET_CONTROL     0x00
++#define KTD202X_REG_FLASH_PERIOD      0x01
++#define KTD202X_REG_PWM1_TIMER                0x02
++#define KTD202X_REG_PWM2_TIMER                0x03
++#define KTD202X_REG_CHANNEL_CTRL      0x04
++#define KTD202X_REG_TRISE_FALL                0x05
++#define KTD202X_REG_LED_IOUT(x)               (0x06 + (x))
++
++/* Register 0 */
++#define KTD202X_TIMER_SLOT_CONTROL_TSLOT1     0x00
++#define KTD202X_TIMER_SLOT_CONTROL_TSLOT2     0x01
++#define KTD202X_TIMER_SLOT_CONTROL_TSLOT3     0x02
++#define KTD202X_TIMER_SLOT_CONTROL_TSLOT4     0x03
++#define KTD202X_RSTR_RESET                    0x07
++
++#define KTD202X_ENABLE_CTRL_WAKE      0x00 /* SCL High & SDA High */
++#define KTD202X_ENABLE_CTRL_SLEEP     0x08 /* SCL High & SDA Toggling */
++
++#define KTD202X_TRISE_FALL_SCALE_NORMAL               0x00
++#define KTD202X_TRISE_FALL_SCALE_SLOW_X2      0x20
++#define KTD202X_TRISE_FALL_SCALE_SLOW_X4      0x40
++#define KTD202X_TRISE_FALL_SCALE_FAST_X8      0x60
++
++/* Register 1 */
++#define KTD202X_FLASH_PERIOD_256_MS_LOG_RAMP  0x00
++
++/* Register 2-3 */
++#define KTD202X_FLASH_ON_TIME_0_4_PERCENT     0x01
++
++/* Register 4 */
++#define KTD202X_CHANNEL_CTRL_MASK(x) (BIT(2 * (x)) | BIT(2 * (x) + 1))
++#define KTD202X_CHANNEL_CTRL_OFF 0x00
++#define KTD202X_CHANNEL_CTRL_ON(x) BIT(2 * (x))
++#define KTD202X_CHANNEL_CTRL_PWM1(x) BIT(2 * (x) + 1)
++#define KTD202X_CHANNEL_CTRL_PWM2(x) (BIT(2 * (x)) | BIT(2 * (x) + 1))
++
++/* Register 5 */
++#define KTD202X_RAMP_TIMES_2_MS                       0x00
++
++/* Register 6-9 */
++#define KTD202X_LED_CURRENT_10_mA             0x4f
++
++#define KTD202X_FLASH_PERIOD_MIN_MS 256
++#define KTD202X_FLASH_PERIOD_STEP_MS 128
++#define KTD202X_FLASH_PERIOD_MAX_STEPS 126
++#define KTD202X_FLASH_ON_MAX 256
++
++#define KTD202X_MAX_BRIGHTNESS 192
++
++static const struct reg_default ktd202x_reg_defaults[] = {
++      { KTD202X_REG_RESET_CONTROL, KTD202X_TIMER_SLOT_CONTROL_TSLOT1 |
++              KTD202X_ENABLE_CTRL_WAKE | KTD202X_TRISE_FALL_SCALE_NORMAL },
++      { KTD202X_REG_FLASH_PERIOD, KTD202X_FLASH_PERIOD_256_MS_LOG_RAMP },
++      { KTD202X_REG_PWM1_TIMER, KTD202X_FLASH_ON_TIME_0_4_PERCENT },
++      { KTD202X_REG_PWM2_TIMER, KTD202X_FLASH_ON_TIME_0_4_PERCENT },
++      { KTD202X_REG_CHANNEL_CTRL, KTD202X_CHANNEL_CTRL_OFF },
++      { KTD202X_REG_TRISE_FALL, KTD202X_RAMP_TIMES_2_MS },
++      { KTD202X_REG_LED_IOUT(0), KTD202X_LED_CURRENT_10_mA },
++      { KTD202X_REG_LED_IOUT(1), KTD202X_LED_CURRENT_10_mA },
++      { KTD202X_REG_LED_IOUT(2), KTD202X_LED_CURRENT_10_mA },
++      { KTD202X_REG_LED_IOUT(3), KTD202X_LED_CURRENT_10_mA },
++};
++
++struct ktd202x_led {
++      struct ktd202x *chip;
++      union {
++              struct led_classdev cdev;
++              struct led_classdev_mc mcdev;
++      };
++      u32 index;
++};
++
++struct ktd202x {
++      struct mutex mutex;
++      struct regulator_bulk_data regulators[2];
++      struct device *dev;
++      struct regmap *regmap;
++      bool enabled;
++      int num_leds;
++      struct ktd202x_led leds[] __counted_by(num_leds);
++};
++
++static int ktd202x_chip_disable(struct ktd202x *chip)
++{
++      int ret;
++
++      if (!chip->enabled)
++              return 0;
++
++      regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_ENABLE_CTRL_SLEEP);
++
++      ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators);
++      if (ret) {
++              dev_err(chip->dev, "Failed to disable regulators: %d\n", ret);
++              return ret;
++      }
++
++      chip->enabled = false;
++      return 0;
++}
++
++static int ktd202x_chip_enable(struct ktd202x *chip)
++{
++      int ret;
++
++      if (chip->enabled)
++              return 0;
++
++      ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), chip->regulators);
++      if (ret) {
++              dev_err(chip->dev, "Failed to enable regulators: %d\n", ret);
++              return ret;
++      }
++      chip->enabled = true;
++
++      ret = regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_ENABLE_CTRL_WAKE);
++
++      if (ret) {
++              dev_err(chip->dev, "Failed to enable the chip: %d\n", ret);
++              ktd202x_chip_disable(chip);
++      }
++
++      return ret;
++}
++
++static bool ktd202x_chip_in_use(struct ktd202x *chip)
++{
++      int i;
++
++      for (i = 0; i < chip->num_leds; i++) {
++              if (chip->leds[i].cdev.brightness)
++                      return true;
++      }
++
++      return false;
++}
++
++static int ktd202x_brightness_set(struct ktd202x_led *led,
++                                struct mc_subled *subleds,
++                                unsigned int num_channels)
++{
++      bool mode_blink = false;
++      int channel;
++      int state;
++      int ret;
++      int i;
++
++      if (ktd202x_chip_in_use(led->chip)) {
++              ret = ktd202x_chip_enable(led->chip);
++              if (ret)
++                      return ret;
++      }
++
++      ret = regmap_read(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL, &state);
++      if (ret)
++              return ret;
++
++      /*
++       * In multicolor case, assume blink mode if PWM is set for at least one
++       * channel because another channel cannot be in state ON at the same time
++       */
++      for (i = 0; i < num_channels; i++) {
++              int channel_state;
++
++              channel = subleds[i].channel;
++              channel_state = (state >> 2 * channel) & KTD202X_CHANNEL_CTRL_MASK(0);
++              if (channel_state == KTD202X_CHANNEL_CTRL_OFF)
++                      continue;
++              mode_blink = channel_state == KTD202X_CHANNEL_CTRL_PWM1(0);
++              break;
++      }
++
++      for (i = 0; i < num_channels; i++) {
++              enum led_brightness brightness;
++              int mode;
++
++              brightness = subleds[i].brightness;
++              channel = subleds[i].channel;
++
++              if (brightness) {
++                      /* Register expects brightness between 0 and MAX_BRIGHTNESS - 1 */
++                      ret = regmap_write(led->chip->regmap, KTD202X_REG_LED_IOUT(channel),
++                                         brightness - 1);
++                      if (ret)
++                              return ret;
++
++                      if (mode_blink)
++                              mode = KTD202X_CHANNEL_CTRL_PWM1(channel);
++                      else
++                              mode = KTD202X_CHANNEL_CTRL_ON(channel);
++              } else {
++                      mode = KTD202X_CHANNEL_CTRL_OFF;
++              }
++              ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL,
++                                       KTD202X_CHANNEL_CTRL_MASK(channel), mode);
++              if (ret)
++                      return ret;
++      }
++
++      if (!ktd202x_chip_in_use(led->chip))
++              return ktd202x_chip_disable(led->chip);
++
++      return 0;
++}
++
++static int ktd202x_brightness_single_set(struct led_classdev *cdev,
++                                       enum led_brightness value)
++{
++      struct ktd202x_led *led = container_of(cdev, struct ktd202x_led, cdev);
++      struct mc_subled info;
++      int ret;
++
++      cdev->brightness = value;
++
++      mutex_lock(&led->chip->mutex);
++
++      info.brightness = value;
++      info.channel = led->index;
++      ret = ktd202x_brightness_set(led, &info, 1);
++
++      mutex_unlock(&led->chip->mutex);
++
++      return ret;
++}
++
++static int ktd202x_brightness_mc_set(struct led_classdev *cdev,
++                                   enum led_brightness value)
++{
++      struct led_classdev_mc *mc = lcdev_to_mccdev(cdev);
++      struct ktd202x_led *led = container_of(mc, struct ktd202x_led, mcdev);
++      int ret;
++
++      cdev->brightness = value;
++
++      mutex_lock(&led->chip->mutex);
++
++      led_mc_calc_color_components(mc, value);
++      ret = ktd202x_brightness_set(led, mc->subled_info, mc->num_colors);
++
++      mutex_unlock(&led->chip->mutex);
++
++      return ret;
++}
++
++static int ktd202x_blink_set(struct ktd202x_led *led, unsigned long *delay_on,
++                           unsigned long *delay_off, struct mc_subled *subleds,
++                           unsigned int num_channels)
++{
++      unsigned long delay_total_ms;
++      int ret, num_steps, on;
++      u8 ctrl_mask = 0;
++      u8 ctrl_pwm1 = 0;
++      u8 ctrl_on = 0;
++      int i;
++
++      mutex_lock(&led->chip->mutex);
++
++      for (i = 0; i < num_channels; i++) {
++              int channel = subleds[i].channel;
++
++              ctrl_mask |= KTD202X_CHANNEL_CTRL_MASK(channel);
++              ctrl_on |= KTD202X_CHANNEL_CTRL_ON(channel);
++              ctrl_pwm1 |= KTD202X_CHANNEL_CTRL_PWM1(channel);
++      }
++
++      /* Never off - brightness is already set, disable blinking */
++      if (!*delay_off) {
++              ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL,
++                                       ctrl_mask, ctrl_on);
++              goto out;
++      }
++
++      /* Convert into values the HW will understand. */
++
++      /* Integer representation of time of flash period */
++      num_steps = (*delay_on + *delay_off - KTD202X_FLASH_PERIOD_MIN_MS) /
++                  KTD202X_FLASH_PERIOD_STEP_MS;
++      num_steps = clamp(num_steps, 0, KTD202X_FLASH_PERIOD_MAX_STEPS);
++
++      /* Integer representation of percentage of LED ON time */
++      on = (*delay_on * KTD202X_FLASH_ON_MAX) / (*delay_on + *delay_off);
++
++      /* Actually used delay_{on,off} values */
++      delay_total_ms = num_steps * KTD202X_FLASH_PERIOD_STEP_MS + KTD202X_FLASH_PERIOD_MIN_MS;
++      *delay_on = (delay_total_ms * on) / KTD202X_FLASH_ON_MAX;
++      *delay_off = delay_total_ms - *delay_on;
++
++      /* Set timings */
++      ret = regmap_write(led->chip->regmap, KTD202X_REG_FLASH_PERIOD, num_steps);
++      if (ret)
++              goto out;
++
++      ret = regmap_write(led->chip->regmap, KTD202X_REG_PWM1_TIMER, on);
++      if (ret)
++              goto out;
++
++      ret = regmap_update_bits(led->chip->regmap, KTD202X_REG_CHANNEL_CTRL,
++                               ctrl_mask, ctrl_pwm1);
++out:
++      mutex_unlock(&led->chip->mutex);
++      return ret;
++}
++
++static int ktd202x_blink_single_set(struct led_classdev *cdev,
++                                  unsigned long *delay_on,
++                                  unsigned long *delay_off)
++{
++      struct ktd202x_led *led = container_of(cdev, struct ktd202x_led, cdev);
++      struct mc_subled info;
++      int ret;
++
++      if (!cdev->brightness) {
++              ret = ktd202x_brightness_single_set(cdev, KTD202X_MAX_BRIGHTNESS);
++              if (ret)
++                      return ret;
++      }
++
++      /* If no blink specified, default to 1 Hz. */
++      if (!*delay_off && !*delay_on) {
++              *delay_off = 500;
++              *delay_on = 500;
++      }
++
++      /* Never on - just set to off */
++      if (!*delay_on)
++              return ktd202x_brightness_single_set(cdev, LED_OFF);
++
++      info.channel = led->index;
++
++      return ktd202x_blink_set(led, delay_on, delay_off, &info, 1);
++}
++
++static int ktd202x_blink_mc_set(struct led_classdev *cdev,
++                              unsigned long *delay_on,
++                              unsigned long *delay_off)
++{
++      struct led_classdev_mc *mc = lcdev_to_mccdev(cdev);
++      struct ktd202x_led *led = container_of(mc, struct ktd202x_led, mcdev);
++      int ret;
++
++      if (!cdev->brightness) {
++              ret = ktd202x_brightness_mc_set(cdev, KTD202X_MAX_BRIGHTNESS);
++              if (ret)
++                      return ret;
++      }
++
++      /* If no blink specified, default to 1 Hz. */
++      if (!*delay_off && !*delay_on) {
++              *delay_off = 500;
++              *delay_on = 500;
++      }
++
++      /* Never on - just set to off */
++      if (!*delay_on)
++              return ktd202x_brightness_mc_set(cdev, LED_OFF);
++
++      return ktd202x_blink_set(led, delay_on, delay_off, mc->subled_info,
++                               mc->num_colors);
++}
++
++static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct device_node *np,
++                               struct ktd202x_led *led, struct led_init_data *init_data)
++{
++      struct led_classdev *cdev;
++      struct device_node *child;
++      struct mc_subled *info;
++      int num_channels;
++      int i = 0;
++
++      num_channels = of_get_available_child_count(np);
++      if (!num_channels || num_channels > chip->num_leds)
++              return -EINVAL;
++
++      info = devm_kcalloc(chip->dev, num_channels, sizeof(*info), GFP_KERNEL);
++      if (!info)
++              return -ENOMEM;
++
++      for_each_available_child_of_node(np, child) {
++              u32 mono_color;
++              u32 reg;
++              int ret;
++
++              ret = of_property_read_u32(child, "reg", &reg);
++              if (ret != 0 || reg >= chip->num_leds) {
++                      dev_err(chip->dev, "invalid 'reg' of %pOFn\n", child);
++                      of_node_put(child);
++                      return -EINVAL;
++              }
++
++              ret = of_property_read_u32(child, "color", &mono_color);
++              if (ret < 0 && ret != -EINVAL) {
++                      dev_err(chip->dev, "failed to parse 'color' of %pOF\n", child);
++                      of_node_put(child);
++                      return ret;
++              }
++
++              info[i].color_index = mono_color;
++              info[i].channel = reg;
++              info[i].intensity = KTD202X_MAX_BRIGHTNESS;
++              i++;
++      }
++
++      led->mcdev.subled_info = info;
++      led->mcdev.num_colors = num_channels;
++
++      cdev = &led->mcdev.led_cdev;
++      cdev->brightness_set_blocking = ktd202x_brightness_mc_set;
++      cdev->blink_set = ktd202x_blink_mc_set;
++
++      return devm_led_classdev_multicolor_register_ext(chip->dev, &led->mcdev, init_data);
++}
++
++static int ktd202x_setup_led_single(struct ktd202x *chip, struct device_node *np,
++                                  struct ktd202x_led *led, struct led_init_data *init_data)
++{
++      struct led_classdev *cdev;
++      u32 reg;
++      int ret;
++
++      ret = of_property_read_u32(np, "reg", &reg);
++      if (ret != 0 || reg >= chip->num_leds) {
++              dev_err(chip->dev, "invalid 'reg' of %pOFn\n", np);
++              return -EINVAL;
++      }
++      led->index = reg;
++
++      cdev = &led->cdev;
++      cdev->brightness_set_blocking = ktd202x_brightness_single_set;
++      cdev->blink_set = ktd202x_blink_single_set;
++
++      return devm_led_classdev_register_ext(chip->dev, &led->cdev, init_data);
++}
++
++static int ktd202x_add_led(struct ktd202x *chip, struct device_node *np, unsigned int index)
++{
++      struct ktd202x_led *led = &chip->leds[index];
++      struct led_init_data init_data = {};
++      struct led_classdev *cdev;
++      u32 color;
++      int ret;
++
++      /* Color property is optional in single color case */
++      ret = of_property_read_u32(np, "color", &color);
++      if (ret < 0 && ret != -EINVAL) {
++              dev_err(chip->dev, "failed to parse 'color' of %pOF\n", np);
++              return ret;
++      }
++
++      led->chip = chip;
++      init_data.fwnode = of_fwnode_handle(np);
++
++      if (color == LED_COLOR_ID_RGB) {
++              cdev = &led->mcdev.led_cdev;
++              ret = ktd202x_setup_led_rgb(chip, np, led, &init_data);
++      } else {
++              cdev = &led->cdev;
++              ret = ktd202x_setup_led_single(chip, np, led, &init_data);
++      }
++
++      if (ret) {
++              dev_err(chip->dev, "unable to register %s\n", cdev->name);
++              return ret;
++      }
++
++      cdev->max_brightness = KTD202X_MAX_BRIGHTNESS;
++
++      return 0;
++}
++
++static int ktd202x_probe_dt(struct ktd202x *chip)
++{
++      struct device_node *np = dev_of_node(chip->dev), *child;
++      int count;
++      int i = 0;
++
++      chip->num_leds = (int)(unsigned long)of_device_get_match_data(chip->dev);
++
++      count = of_get_available_child_count(np);
++      if (!count || count > chip->num_leds)
++              return -EINVAL;
++
++      regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET);
++
++      /* Allow the device to execute the complete reset */
++      usleep_range(200, 300);
++
++      for_each_available_child_of_node(np, child) {
++              int ret = ktd202x_add_led(chip, child, i);
++
++              if (ret) {
++                      of_node_put(child);
++                      return ret;
++              }
++              i++;
++      }
++
++      return 0;
++}
++
++static const struct regmap_config ktd202x_regmap_config = {
++      .reg_bits = 8,
++      .val_bits = 8,
++      .max_register = 0x09,
++      .cache_type = REGCACHE_FLAT,
++      .reg_defaults = ktd202x_reg_defaults,
++      .num_reg_defaults = ARRAY_SIZE(ktd202x_reg_defaults),
++};
++
++static int ktd202x_probe(struct i2c_client *client)
++{
++      struct device *dev = &client->dev;
++      struct ktd202x *chip;
++      int count;
++      int ret;
++
++      count = device_get_child_node_count(dev);
++      if (!count || count > KTD202X_MAX_LEDS)
++              return dev_err_probe(dev, -EINVAL, "Incorrect number of leds (%d)", count);
++
++      chip = devm_kzalloc(dev, struct_size(chip, leds, count), GFP_KERNEL);
++      if (!chip)
++              return -ENOMEM;
++
++      chip->dev = dev;
++      i2c_set_clientdata(client, chip);
++
++      chip->regmap = devm_regmap_init_i2c(client, &ktd202x_regmap_config);
++      if (IS_ERR(chip->regmap)) {
++              ret = dev_err_probe(dev, PTR_ERR(chip->regmap),
++                                  "Failed to allocate register map.\n");
++              return ret;
++      }
++
++      chip->regulators[0].supply = "vin";
++      chip->regulators[1].supply = "vio";
++      ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(chip->regulators), chip->regulators);
++      if (ret < 0) {
++              dev_err_probe(dev, ret, "Failed to request regulators.\n");
++              return ret;
++      }
++
++      ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), chip->regulators);
++      if (ret) {
++              dev_err_probe(dev, ret, "Failed to enable regulators.\n");
++              return ret;
++      }
++
++      ret = ktd202x_probe_dt(chip);
++      if (ret < 0) {
++              regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators);
++              return ret;
++      }
++
++      ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators);
++      if (ret) {
++              dev_err_probe(dev, ret, "Failed to disable regulators.\n");
++              return ret;
++      }
++
++      mutex_init(&chip->mutex);
++
++      return 0;
++}
++
++static void ktd202x_remove(struct i2c_client *client)
++{
++      struct ktd202x *chip = i2c_get_clientdata(client);
++
++      ktd202x_chip_disable(chip);
++
++      mutex_destroy(&chip->mutex);
++}
++
++static void ktd202x_shutdown(struct i2c_client *client)
++{
++      struct ktd202x *chip = i2c_get_clientdata(client);
++
++      /* Reset registers to make sure all LEDs are off before shutdown */
++      regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET);
++}
++
++static const struct of_device_id ktd202x_match_table[] = {
++      { .compatible = "kinetic,ktd2026", .data = (void *)KTD2026_NUM_LEDS },
++      { .compatible = "kinetic,ktd2027", .data = (void *)KTD2027_NUM_LEDS },
++      {},
++};
++MODULE_DEVICE_TABLE(of, ktd202x_match_table);
++
++static struct i2c_driver ktd202x_driver = {
++      .driver = {
++              .name = "leds-ktd202x",
++              .of_match_table = ktd202x_match_table,
++      },
++      .probe = ktd202x_probe,
++      .remove = ktd202x_remove,
++      .shutdown = ktd202x_shutdown,
++};
++module_i2c_driver(ktd202x_driver);
++
++MODULE_AUTHOR("André Apitzsch <git@apitzsch.eu>");
++MODULE_DESCRIPTION("Kinetic KTD2026/7 LED driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.6/861-v6.10-leds-rgb-leds-ktd202x-get-device-properties-through-fwnode.patch b/target/linux/generic/backport-6.6/861-v6.10-leds-rgb-leds-ktd202x-get-device-properties-through-fwnode.patch
new file mode 100644 (file)
index 0000000..92b20c3
--- /dev/null
@@ -0,0 +1,221 @@
+From f14aa5ea415b8add245e976bfab96a12986c6843 Mon Sep 17 00:00:00 2001
+From: Kate Hsuan <hpa@redhat.com>
+Date: Fri, 31 May 2024 13:41:19 +0200
+Subject: leds: rgb: leds-ktd202x: Get device properties through fwnode to
+ support ACPI
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This LED controller is installed on a Xiaomi pad2 and it is an x86
+platform. The original driver is based on the device tree and can't be
+used for this ACPI based system. This patch migrated the driver to use
+fwnode to access the properties. Moreover, the fwnode API supports the
+device tree so this work won't affect the original implementations.
+
+Signed-off-by: Kate Hsuan <hpa@redhat.com>
+Tested-by: André Apitzsch <git@apitzsch.eu> # on BQ Aquaris M5
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://lore.kernel.org/r/20240531114124.45346-2-hdegoede@redhat.com
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/rgb/Kconfig        |  1 -
+ drivers/leds/rgb/leds-ktd202x.c | 64 ++++++++++++++++++++++-------------------
+ 2 files changed, 34 insertions(+), 31 deletions(-)
+
+(limited to 'drivers/leds/rgb')
+
+--- a/drivers/leds/rgb/Kconfig
++++ b/drivers/leds/rgb/Kconfig
+@@ -17,7 +17,6 @@ config LEDS_GROUP_MULTICOLOR
+ config LEDS_KTD202X
+       tristate "LED support for KTD202x Chips"
+       depends on I2C
+-      depends on OF
+       select REGMAP_I2C
+       help
+         This option enables support for the Kinetic KTD2026/KTD2027
+--- a/drivers/leds/rgb/leds-ktd202x.c
++++ b/drivers/leds/rgb/leds-ktd202x.c
+@@ -99,7 +99,7 @@ struct ktd202x {
+       struct device *dev;
+       struct regmap *regmap;
+       bool enabled;
+-      int num_leds;
++      unsigned long num_leds;
+       struct ktd202x_led leds[] __counted_by(num_leds);
+ };
+@@ -381,16 +381,19 @@ static int ktd202x_blink_mc_set(struct l
+                                mc->num_colors);
+ }
+-static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct device_node *np,
++static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct fwnode_handle *fwnode,
+                                struct ktd202x_led *led, struct led_init_data *init_data)
+ {
++      struct fwnode_handle *child;
+       struct led_classdev *cdev;
+-      struct device_node *child;
+       struct mc_subled *info;
+       int num_channels;
+       int i = 0;
+-      num_channels = of_get_available_child_count(np);
++      num_channels = 0;
++      fwnode_for_each_available_child_node(fwnode, child)
++              num_channels++;
++
+       if (!num_channels || num_channels > chip->num_leds)
+               return -EINVAL;
+@@ -398,22 +401,22 @@ static int ktd202x_setup_led_rgb(struct
+       if (!info)
+               return -ENOMEM;
+-      for_each_available_child_of_node(np, child) {
++      fwnode_for_each_available_child_node(fwnode, child) {
+               u32 mono_color;
+               u32 reg;
+               int ret;
+-              ret = of_property_read_u32(child, "reg", &reg);
++              ret = fwnode_property_read_u32(child, "reg", &reg);
+               if (ret != 0 || reg >= chip->num_leds) {
+-                      dev_err(chip->dev, "invalid 'reg' of %pOFn\n", child);
+-                      of_node_put(child);
+-                      return -EINVAL;
++                      dev_err(chip->dev, "invalid 'reg' of %pfw\n", child);
++                      fwnode_handle_put(child);
++                      return ret;
+               }
+-              ret = of_property_read_u32(child, "color", &mono_color);
++              ret = fwnode_property_read_u32(child, "color", &mono_color);
+               if (ret < 0 && ret != -EINVAL) {
+-                      dev_err(chip->dev, "failed to parse 'color' of %pOF\n", child);
+-                      of_node_put(child);
++                      dev_err(chip->dev, "failed to parse 'color' of %pfw\n", child);
++                      fwnode_handle_put(child);
+                       return ret;
+               }
+@@ -433,16 +436,16 @@ static int ktd202x_setup_led_rgb(struct
+       return devm_led_classdev_multicolor_register_ext(chip->dev, &led->mcdev, init_data);
+ }
+-static int ktd202x_setup_led_single(struct ktd202x *chip, struct device_node *np,
++static int ktd202x_setup_led_single(struct ktd202x *chip, struct fwnode_handle *fwnode,
+                                   struct ktd202x_led *led, struct led_init_data *init_data)
+ {
+       struct led_classdev *cdev;
+       u32 reg;
+       int ret;
+-      ret = of_property_read_u32(np, "reg", &reg);
++      ret = fwnode_property_read_u32(fwnode, "reg", &reg);
+       if (ret != 0 || reg >= chip->num_leds) {
+-              dev_err(chip->dev, "invalid 'reg' of %pOFn\n", np);
++              dev_err(chip->dev, "invalid 'reg' of %pfw\n", fwnode);
+               return -EINVAL;
+       }
+       led->index = reg;
+@@ -454,7 +457,7 @@ static int ktd202x_setup_led_single(stru
+       return devm_led_classdev_register_ext(chip->dev, &led->cdev, init_data);
+ }
+-static int ktd202x_add_led(struct ktd202x *chip, struct device_node *np, unsigned int index)
++static int ktd202x_add_led(struct ktd202x *chip, struct fwnode_handle *fwnode, unsigned int index)
+ {
+       struct ktd202x_led *led = &chip->leds[index];
+       struct led_init_data init_data = {};
+@@ -463,21 +466,21 @@ static int ktd202x_add_led(struct ktd202
+       int ret;
+       /* Color property is optional in single color case */
+-      ret = of_property_read_u32(np, "color", &color);
++      ret = fwnode_property_read_u32(fwnode, "color", &color);
+       if (ret < 0 && ret != -EINVAL) {
+-              dev_err(chip->dev, "failed to parse 'color' of %pOF\n", np);
++              dev_err(chip->dev, "failed to parse 'color' of %pfw\n", fwnode);
+               return ret;
+       }
+       led->chip = chip;
+-      init_data.fwnode = of_fwnode_handle(np);
++      init_data.fwnode = fwnode;
+       if (color == LED_COLOR_ID_RGB) {
+               cdev = &led->mcdev.led_cdev;
+-              ret = ktd202x_setup_led_rgb(chip, np, led, &init_data);
++              ret = ktd202x_setup_led_rgb(chip, fwnode, led, &init_data);
+       } else {
+               cdev = &led->cdev;
+-              ret = ktd202x_setup_led_single(chip, np, led, &init_data);
++              ret = ktd202x_setup_led_single(chip, fwnode, led, &init_data);
+       }
+       if (ret) {
+@@ -490,15 +493,14 @@ static int ktd202x_add_led(struct ktd202
+       return 0;
+ }
+-static int ktd202x_probe_dt(struct ktd202x *chip)
++static int ktd202x_probe_fw(struct ktd202x *chip)
+ {
+-      struct device_node *np = dev_of_node(chip->dev), *child;
++      struct fwnode_handle *child;
++      struct device *dev = chip->dev;
+       int count;
+       int i = 0;
+-      chip->num_leds = (int)(unsigned long)of_device_get_match_data(chip->dev);
+-
+-      count = of_get_available_child_count(np);
++      count = device_get_child_node_count(dev);
+       if (!count || count > chip->num_leds)
+               return -EINVAL;
+@@ -507,11 +509,11 @@ static int ktd202x_probe_dt(struct ktd20
+       /* Allow the device to execute the complete reset */
+       usleep_range(200, 300);
+-      for_each_available_child_of_node(np, child) {
++      device_for_each_child_node(dev, child) {
+               int ret = ktd202x_add_led(chip, child, i);
+               if (ret) {
+-                      of_node_put(child);
++                      fwnode_handle_put(child);
+                       return ret;
+               }
+               i++;
+@@ -554,6 +556,8 @@ static int ktd202x_probe(struct i2c_clie
+               return ret;
+       }
++      chip->num_leds = (unsigned long)i2c_get_match_data(client);
++
+       chip->regulators[0].supply = "vin";
+       chip->regulators[1].supply = "vio";
+       ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(chip->regulators), chip->regulators);
+@@ -568,7 +572,7 @@ static int ktd202x_probe(struct i2c_clie
+               return ret;
+       }
+-      ret = ktd202x_probe_dt(chip);
++      ret = ktd202x_probe_fw(chip);
+       if (ret < 0) {
+               regulator_bulk_disable(ARRAY_SIZE(chip->regulators), chip->regulators);
+               return ret;
+@@ -605,7 +609,7 @@ static void ktd202x_shutdown(struct i2c_
+ static const struct of_device_id ktd202x_match_table[] = {
+       { .compatible = "kinetic,ktd2026", .data = (void *)KTD2026_NUM_LEDS },
+       { .compatible = "kinetic,ktd2027", .data = (void *)KTD2027_NUM_LEDS },
+-      {},
++      {}
+ };
+ MODULE_DEVICE_TABLE(of, ktd202x_match_table);
diff --git a/target/linux/generic/backport-6.6/862-v6.10-leds-rgb-leds-ktd202x-i2c-id-tables-for-ktd2026-and-2027.patch b/target/linux/generic/backport-6.6/862-v6.10-leds-rgb-leds-ktd202x-i2c-id-tables-for-ktd2026-and-2027.patch
new file mode 100644 (file)
index 0000000..223ccd8
--- /dev/null
@@ -0,0 +1,49 @@
+From 75bd07aef47e1a984229e6ec702e8b9aee0226e4 Mon Sep 17 00:00:00 2001
+From: Kate Hsuan <hpa@redhat.com>
+Date: Fri, 31 May 2024 13:41:20 +0200
+Subject: leds: rgb: leds-ktd202x: I2C ID tables for KTD2026 and 2027
+
+Add an i2c_device_id id_table to match manually instantiated
+(non device-tree / ACPI instantiated) KTD202x controllers as
+found on some x86 boards.
+
+This table shows the maximum support LED channel for KTD2026
+(three LEDs) and KTD-2027 (4 LEDs).
+
+Link: https://www.kinet-ic.com/uploads/KTD2026-7-04h.pdf
+Signed-off-by: Kate Hsuan <hpa@redhat.com>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://lore.kernel.org/r/20240531114124.45346-3-hdegoede@redhat.com
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/rgb/leds-ktd202x.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+(limited to 'drivers/leds/rgb')
+
+--- a/drivers/leds/rgb/leds-ktd202x.c
++++ b/drivers/leds/rgb/leds-ktd202x.c
+@@ -606,6 +606,13 @@ static void ktd202x_shutdown(struct i2c_
+       regmap_write(chip->regmap, KTD202X_REG_RESET_CONTROL, KTD202X_RSTR_RESET);
+ }
++static const struct i2c_device_id ktd202x_id[] = {
++      {"ktd2026", KTD2026_NUM_LEDS},
++      {"ktd2027", KTD2027_NUM_LEDS},
++      {}
++};
++MODULE_DEVICE_TABLE(i2c, ktd202x_id);
++
+ static const struct of_device_id ktd202x_match_table[] = {
+       { .compatible = "kinetic,ktd2026", .data = (void *)KTD2026_NUM_LEDS },
+       { .compatible = "kinetic,ktd2027", .data = (void *)KTD2027_NUM_LEDS },
+@@ -621,6 +628,7 @@ static struct i2c_driver ktd202x_driver
+       .probe = ktd202x_probe,
+       .remove = ktd202x_remove,
+       .shutdown = ktd202x_shutdown,
++      .id_table = ktd202x_id,
+ };
+ module_i2c_driver(ktd202x_driver);
diff --git a/target/linux/generic/backport-6.6/863-v6.10-leds-rgb-leds-ktd202x-initialize-mutex-earlier.patch b/target/linux/generic/backport-6.6/863-v6.10-leds-rgb-leds-ktd202x-initialize-mutex-earlier.patch
new file mode 100644 (file)
index 0000000..533b0f0
--- /dev/null
@@ -0,0 +1,62 @@
+From e1b08c6f5b92d408a9fcc1030a340caeb9852250 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Fri, 31 May 2024 13:41:21 +0200
+Subject: leds: rgb: leds-ktd202x: Initialize mutex earlier
+
+The mutex must be initialized before the LED class device is registered
+otherwise there is a race where it may get used before it is initialized:
+
+ DEBUG_LOCKS_WARN_ON(lock->magic != lock)
+ WARNING: CPU: 2 PID: 2045 at kernel/locking/mutex.c:587 __mutex_lock
+ ...
+ RIP: 0010:__mutex_lock+0x7db/0xc10
+ ...
+ set_brightness_delayed_set_brightness.part.0+0x17/0x60
+ set_brightness_delayed+0xf1/0x100
+ process_one_work+0x222/0x5a0
+
+Move the mutex_init() call earlier to avoid this race condition and
+switch to devm_mutex_init() to avoid the need to add error-exit
+cleanup to probe() if probe() fails later on.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Link: https://lore.kernel.org/r/20240531114124.45346-4-hdegoede@redhat.com
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/rgb/leds-ktd202x.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+(limited to 'drivers/leds/rgb')
+
+--- a/drivers/leds/rgb/leds-ktd202x.c
++++ b/drivers/leds/rgb/leds-ktd202x.c
+@@ -556,6 +556,10 @@ static int ktd202x_probe(struct i2c_clie
+               return ret;
+       }
++      ret = devm_mutex_init(dev, &chip->mutex);
++      if (ret)
++              return ret;
++
+       chip->num_leds = (unsigned long)i2c_get_match_data(client);
+       chip->regulators[0].supply = "vin";
+@@ -584,8 +588,6 @@ static int ktd202x_probe(struct i2c_clie
+               return ret;
+       }
+-      mutex_init(&chip->mutex);
+-
+       return 0;
+ }
+@@ -594,8 +596,6 @@ static void ktd202x_remove(struct i2c_cl
+       struct ktd202x *chip = i2c_get_clientdata(client);
+       ktd202x_chip_disable(chip);
+-
+-      mutex_destroy(&chip->mutex);
+ }
+ static void ktd202x_shutdown(struct i2c_client *client)
diff --git a/target/linux/generic/backport-6.6/894-v6.8-net-ethtool-implement-ethtool_puts.patch b/target/linux/generic/backport-6.6/894-v6.8-net-ethtool-implement-ethtool_puts.patch
new file mode 100644 (file)
index 0000000..8e57193
--- /dev/null
@@ -0,0 +1,139 @@
+From mboxrd@z Thu Jan  1 00:00:00 1970
+Authentication-Results: smtp.subspace.kernel.org;
+       dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="sMUeie/T"
+Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49])
+       by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 84BB8D6D
+       for <bpf@vger.kernel.org>; Wed,  6 Dec 2023 15:16:16 -0800 (PST)
+Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-db5416d0fccso403298276.1
+        for <bpf@vger.kernel.org>; Wed, 06 Dec 2023 15:16:16 -0800 (PST)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+        d=google.com; s=20230601; t=1701904575; x=1702509375; darn=vger.kernel.org;
+        h=cc:to:from:subject:message-id:references:mime-version:in-reply-to
+         :date:from:to:cc:subject:date:message-id:reply-to;
+        bh=/7eYcPC4ZNNyPcPPs0B5tDplF0arxw3r0vINNNou0rY=;
+        b=sMUeie/TxdytzC0EyT11QWi1TqTtiv7KCTs1F2vLmUUvPKNA3+1MHFo8ECW+0gQuDE
+         FGrgdZKGK5mXQgkF0N3JiSLvKO8tpQOIB57JLCG5IVy5dr2vVv0ExU3Dag2Cc4oBIBIO
+         w/cH95O1oPlvluIpATmAsxenVr7mFomU63BqYiRGLaEhWeb2hJ636GO8lubtsDfdFFoi
+         GPOL2tQwV93VnqmywBBpFaNAULN0UoCFhfkKv5prvpkXq19sWI7zyorVZ+rdTYem5m4T
+         dXsDaLXPtC3Dh2JOad1duSQIah/wCHYYUcV3IoFhwj2y0Uk/TTCrnZPORweSADcEy6Ho
+         vDrA==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+        d=1e100.net; s=20230601; t=1701904575; x=1702509375;
+        h=cc:to:from:subject:message-id:references:mime-version:in-reply-to
+         :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;
+        bh=/7eYcPC4ZNNyPcPPs0B5tDplF0arxw3r0vINNNou0rY=;
+        b=Dmc6aSntxPlxAk72zVO1G9WoZnFtLolxENlLscYYAHG3VE+PQ8gGN2rPzcGoKb2Btb
+         4b0PvjOzSlPQyghahdhdlz04RtAeeGG/MkfNiYjFql5OifIoovb51kroiPYrVsa7Ps7Y
+         +Pxug0+NPdTm5s9TNz940ZKl3GRME8UTmVxpWJRX03XMOqb6Wgsh2SK9ahXKc4yRsi62
+         3a3J72WmmSgvimxwM/99fXwvoUQpiv2J1xCoqc1Ng4q4qSuZvzmHN7ZTGaUhLxOqLeLK
+         3W4RKHW6rZ7UjppuB6I3NXW+D344By2rdKp1sRXpjdQ0GS3YUcvlRETcJBXJudHfQP5Y
+         CLOw==
+X-Gm-Message-State: AOJu0YzdCTLdwny+N99zeMgyKqFsEZhfIhL2cbgKA6zC1U/OLkxxRLoM
+       XrYVBC9DmxCGmP4o+M/Z/kHUew/9faHlCiLGxw==
+X-Google-Smtp-Source: AGHT+IFRXxBV6JuX5Cl/k2o1+WKkCwkR8j20MJSkmoGCedPAtqFttH8OVh1/6vdfnq8MPN++A2h89peZQhyG8OsJ8A==
+X-Received: from jstitt-linux1.c.googlers.com ([fda3:e722:ac3:cc00:2b:ff92:c0a8:23b5])
+ (user=justinstitt job=sendgmr) by 2002:a25:dac7:0:b0:da0:3117:f35 with SMTP
+ id n190-20020a25dac7000000b00da031170f35mr28652ybf.3.1701904575576; Wed, 06
+ Dec 2023 15:16:15 -0800 (PST)
+Date: Wed, 06 Dec 2023 23:16:10 +0000
+In-Reply-To: <20231206-ethtool_puts_impl-v5-0-5a2528e17bf8@google.com>
+Precedence: bulk
+X-Mailing-List: bpf@vger.kernel.org
+List-Id: <bpf.vger.kernel.org>
+List-Subscribe: <mailto:bpf+subscribe@vger.kernel.org>
+List-Unsubscribe: <mailto:bpf+unsubscribe@vger.kernel.org>
+Mime-Version: 1.0
+References: <20231206-ethtool_puts_impl-v5-0-5a2528e17bf8@google.com>
+X-Developer-Key: i=justinstitt@google.com; a=ed25519; pk=tC3hNkJQTpNX/gLKxTNQKDmiQl6QjBNCGKJINqAdJsE=
+X-Developer-Signature: v=1; a=ed25519-sha256; t=1701904573; l=1840;
+ i=justinstitt@google.com; s=20230717; h=from:subject:message-id;
+ bh=UMdetIL2ZsPIkSodqhw2fM21NHJVjCu0lRImFuNhVoM=; b=a8rMnXfVVQ5gsxHWG4WRMwOLxZgflqXZtNuKx26vv4DwYvvCtCiYjl3f1frOjV/Ul2kaxq5g/
+ b/UOv678JKCDASVokxG5GJifAnU7/kqRxdhcwfRkrD8RUfcsmiZOfyF
+X-Mailer: b4 0.12.3
+Message-ID: <20231206-ethtool_puts_impl-v5-1-5a2528e17bf8@google.com>
+Subject: [PATCH net-next v5 1/3] ethtool: Implement ethtool_puts()
+From: justinstitt@google.com
+To: "David S. Miller" <davem@davemloft.net>, Eric Dumazet <edumazet@google.com>, 
+       Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>, Shay Agroskin <shayagr@amazon.com>, 
+       Arthur Kiyanovski <akiyano@amazon.com>, David Arinzon <darinzon@amazon.com>, Noam Dagan <ndagan@amazon.com>, 
+       Saeed Bishara <saeedb@amazon.com>, Rasesh Mody <rmody@marvell.com>, 
+       Sudarsana Kalluru <skalluru@marvell.com>, GR-Linux-NIC-Dev@marvell.com, 
+       Dimitris Michailidis <dmichail@fungible.com>, Yisen Zhuang <yisen.zhuang@huawei.com>, 
+       Salil Mehta <salil.mehta@huawei.com>, Jesse Brandeburg <jesse.brandeburg@intel.com>, 
+       Tony Nguyen <anthony.l.nguyen@intel.com>, Louis Peens <louis.peens@corigine.com>, 
+       Shannon Nelson <shannon.nelson@amd.com>, Brett Creeley <brett.creeley@amd.com>, drivers@pensando.io, 
+       "K. Y. Srinivasan" <kys@microsoft.com>, Haiyang Zhang <haiyangz@microsoft.com>, Wei Liu <wei.liu@kernel.org>, 
+       Dexuan Cui <decui@microsoft.com>, Ronak Doshi <doshir@vmware.com>, 
+       VMware PV-Drivers Reviewers <pv-drivers@vmware.com>, Andy Whitcroft <apw@canonical.com>, Joe Perches <joe@perches.com>, 
+       Dwaipayan Ray <dwaipayanray1@gmail.com>, Lukas Bulwahn <lukas.bulwahn@gmail.com>, 
+       Hauke Mehrtens <hauke@hauke-m.de>, Andrew Lunn <andrew@lunn.ch>, 
+       Florian Fainelli <f.fainelli@gmail.com>, Vladimir Oltean <olteanv@gmail.com>, 
+       "=?utf-8?q?Ar=C4=B1n=C3=A7_=C3=9CNAL?=" <arinc.unal@arinc9.com>, Daniel Golle <daniel@makrotopia.org>, 
+       Landen Chao <Landen.Chao@mediatek.com>, DENG Qingfang <dqfext@gmail.com>, 
+       Sean Wang <sean.wang@mediatek.com>, Matthias Brugger <matthias.bgg@gmail.com>, 
+       AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>, 
+       Linus Walleij <linus.walleij@linaro.org>, 
+       "=?utf-8?q?Alvin_=C5=A0ipraga?=" <alsi@bang-olufsen.dk>, Wei Fang <wei.fang@nxp.com>, 
+       Shenwei Wang <shenwei.wang@nxp.com>, Clark Wang <xiaoning.wang@nxp.com>, 
+       NXP Linux Team <linux-imx@nxp.com>, Lars Povlsen <lars.povlsen@microchip.com>, 
+       Steen Hegelund <Steen.Hegelund@microchip.com>, Daniel Machon <daniel.machon@microchip.com>, 
+       UNGLinuxDriver@microchip.com, Jiawen Wu <jiawenwu@trustnetic.com>, 
+       Mengyuan Lou <mengyuanlou@net-swift.com>, Heiner Kallweit <hkallweit1@gmail.com>, 
+       Russell King <linux@armlinux.org.uk>, Alexei Starovoitov <ast@kernel.org>, 
+       Daniel Borkmann <daniel@iogearbox.net>, Jesper Dangaard Brouer <hawk@kernel.org>, 
+       John Fastabend <john.fastabend@gmail.com>
+Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, 
+       Nick Desaulniers <ndesaulniers@google.com>, Nathan Chancellor <nathan@kernel.org>, 
+       Kees Cook <keescook@chromium.org>, intel-wired-lan@lists.osuosl.org, 
+       oss-drivers@corigine.com, linux-hyperv@vger.kernel.org, 
+       linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, 
+       bpf@vger.kernel.org, Justin Stitt <justinstitt@google.com>
+Content-Type: text/plain; charset="utf-8"
+
+Use strscpy() to implement ethtool_puts().
+
+Functionally the same as ethtool_sprintf() when it's used with two
+arguments or with just "%s" format specifier.
+
+Signed-off-by: Justin Stitt <justinstitt@google.com>
+---
+ include/linux/ethtool.h | 13 +++++++++++++
+ net/ethtool/ioctl.c     |  7 +++++++
+ 2 files changed, 20 insertions(+)
+
+--- a/include/linux/ethtool.h
++++ b/include/linux/ethtool.h
+@@ -1052,4 +1052,17 @@ static inline int ethtool_mm_frag_size_m
+  * next string.
+  */
+ extern __printf(2, 3) void ethtool_sprintf(u8 **data, const char *fmt, ...);
++
++/**
++ * ethtool_puts - Write string to ethtool string data
++ * @data: Pointer to a pointer to the start of string to update
++ * @str: String to write
++ *
++ * Write string to *data without a trailing newline. Update *data
++ * to point at start of next string.
++ *
++ * Prefer this function to ethtool_sprintf() when given only
++ * two arguments or if @fmt is just "%s".
++ */
++extern void ethtool_puts(u8 **data, const char *str);
+ #endif /* _LINUX_ETHTOOL_H */
+--- a/net/ethtool/ioctl.c
++++ b/net/ethtool/ioctl.c
+@@ -1994,6 +1994,13 @@ __printf(2, 3) void ethtool_sprintf(u8 *
+ }
+ EXPORT_SYMBOL(ethtool_sprintf);
++void ethtool_puts(u8 **data, const char *str)
++{
++      strscpy(*data, str, ETH_GSTRING_LEN);
++      *data += ETH_GSTRING_LEN;
++}
++EXPORT_SYMBOL(ethtool_puts);
++
+ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
+ {
+       struct ethtool_value id;
diff --git a/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch b/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch
new file mode 100644 (file)
index 0000000..b86dbea
--- /dev/null
@@ -0,0 +1,60 @@
+From 1a7aa058bc92f0edae7a0d1ef1a7b05aec0c643a Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:27:52 +0000
+Subject: [PATCH 1/7] net: phy: add possible interfaces
+
+Add a possible_interfaces member to struct phy_device to indicate which
+interfaces a clause 45 PHY may switch between depending on the media.
+This must be populated by the PHY driver by the time the .config_init()
+method completes according to the PHYs host-side configuration.
+
+For example, the Marvell 88x3310 PHY can switch between 10GBASE-R,
+5GBASE-R, 2500BASE-X, and SGMII on the host side depending on the media
+side speed, so all these interface modes are set in the
+possible_interfaces member.
+
+This allows phylib users (such as phylink) to know in advance which
+interface modes to expect, which allows them to appropriately restrict
+the advertised link modes according to the capabilities of other parts
+of the link.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VHk-00DDLN-I7@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phy_device.c | 2 ++
+ include/linux/phy.h          | 3 +++
+ 2 files changed, 5 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1247,6 +1247,8 @@ int phy_init_hw(struct phy_device *phyde
+       if (ret < 0)
+               return ret;
++      phy_interface_zero(phydev->possible_interfaces);
++
+       if (phydev->drv->config_init) {
+               ret = phydev->drv->config_init(phydev);
+               if (ret < 0)
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -609,6 +609,8 @@ struct macsec_ops;
+  * @irq_rerun: Flag indicating interrupts occurred while PHY was suspended,
+  *             requiring a rerun of the interrupt handler after resume
+  * @interface: enum phy_interface_t value
++ * @possible_interfaces: bitmap if interface modes that the attached PHY
++ *                     will switch between depending on media speed.
+  * @skb: Netlink message for cable diagnostics
+  * @nest: Netlink nest used for cable diagnostics
+  * @ehdr: nNtlink header for cable diagnostics
+@@ -678,6 +680,7 @@ struct phy_device {
+       u32 dev_flags;
+       phy_interface_t interface;
++      DECLARE_PHY_INTERFACE_MASK(possible_interfaces);
+       /*
+        * forced speed & duplex (no autoneg)
diff --git a/target/linux/generic/backport-6.6/895-02-v6.8-net-phylink-use-for_each_set_bit.patch b/target/linux/generic/backport-6.6/895-02-v6.8-net-phylink-use-for_each_set_bit.patch
new file mode 100644 (file)
index 0000000..397780f
--- /dev/null
@@ -0,0 +1,46 @@
+From 85631f5b33f2acce7d42dec1d0a062ab40de95b8 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Sun, 19 Nov 2023 21:07:43 +0000
+Subject: [PATCH 2/7] net: phylink: use for_each_set_bit()
+
+Use for_each_set_bit() rather than open coding the for() test_bit()
+loop.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Link: https://lore.kernel.org/r/E1r4p15-00Cpxe-C7@rmk-PC.armlinux.org.uk
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/phylink.c | 18 ++++++++----------
+ 1 file changed, 8 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -712,18 +712,16 @@ static int phylink_validate_mask(struct
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
+       struct phylink_link_state t;
+-      int intf;
++      int interface;
+-      for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
+-              if (test_bit(intf, interfaces)) {
+-                      linkmode_copy(s, supported);
++      for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) {
++              linkmode_copy(s, supported);
+-                      t = *state;
+-                      t.interface = intf;
+-                      if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
+-                              linkmode_or(all_s, all_s, s);
+-                              linkmode_or(all_adv, all_adv, t.advertising);
+-                      }
++              t = *state;
++              t.interface = interface;
++              if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
++                      linkmode_or(all_s, all_s, s);
++                      linkmode_or(all_adv, all_adv, t.advertising);
+               }
+       }
diff --git a/target/linux/generic/backport-6.6/895-03-v6.8-net-phylink-split-out-per-interface-validation.patch b/target/linux/generic/backport-6.6/895-03-v6.8-net-phylink-split-out-per-interface-validation.patch
new file mode 100644 (file)
index 0000000..33f64e8
--- /dev/null
@@ -0,0 +1,76 @@
+From d4788b4383ce5caeb4e68818357c81a02117a3f9 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:19 +0000
+Subject: [PATCH 3/7] net: phylink: split out per-interface validation
+
+Split out the internals of phylink_validate_mask() to make the code
+easier to read.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIB-00DDLr-7g@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 42 ++++++++++++++++++++++++++++-----------
+ 1 file changed, 30 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -704,26 +704,44 @@ static int phylink_validate_mac_and_pcs(
+       return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+ }
++static void phylink_validate_one(struct phylink *pl,
++                               const unsigned long *supported,
++                               const struct phylink_link_state *state,
++                               phy_interface_t interface,
++                               unsigned long *accum_supported,
++                               unsigned long *accum_advertising)
++{
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_supported);
++      struct phylink_link_state tmp_state;
++
++      linkmode_copy(tmp_supported, supported);
++
++      tmp_state = *state;
++      tmp_state.interface = interface;
++
++      if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) {
++              phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n",
++                          interface, phy_modes(interface),
++                          phy_rate_matching_to_str(tmp_state.rate_matching),
++                          __ETHTOOL_LINK_MODE_MASK_NBITS, tmp_supported);
++
++              linkmode_or(accum_supported, accum_supported, tmp_supported);
++              linkmode_or(accum_advertising, accum_advertising,
++                          tmp_state.advertising);
++      }
++}
++
+ static int phylink_validate_mask(struct phylink *pl, unsigned long *supported,
+                                struct phylink_link_state *state,
+                                const unsigned long *interfaces)
+ {
+       __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 interface;
+-      for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) {
+-              linkmode_copy(s, supported);
+-
+-              t = *state;
+-              t.interface = interface;
+-              if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
+-                      linkmode_or(all_s, all_s, s);
+-                      linkmode_or(all_adv, all_adv, t.advertising);
+-              }
+-      }
++      for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
++              phylink_validate_one(pl, supported, state, interface,
++                                   all_s, all_adv);
+       linkmode_copy(supported, all_s);
+       linkmode_copy(state->advertising, all_adv);
diff --git a/target/linux/generic/backport-6.6/895-04-v6.8-net-phylink-pass-PHY-into-phylink_validate_one.patch b/target/linux/generic/backport-6.6/895-04-v6.8-net-phylink-pass-PHY-into-phylink_validate_one.patch
new file mode 100644 (file)
index 0000000..e3915f0
--- /dev/null
@@ -0,0 +1,47 @@
+From ce7273c31fadb3143fc80c96a72a42adc19c2757 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:24 +0000
+Subject: [PATCH 4/7] net: phylink: pass PHY into phylink_validate_one()
+
+Pass the phy (if any) into phylink_validate_one() so that we can
+validate each interface with its rate matching setting.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIG-00DDLx-Cb@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -704,7 +704,7 @@ static int phylink_validate_mac_and_pcs(
+       return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+ }
+-static void phylink_validate_one(struct phylink *pl,
++static void phylink_validate_one(struct phylink *pl, struct phy_device *phy,
+                                const unsigned long *supported,
+                                const struct phylink_link_state *state,
+                                phy_interface_t interface,
+@@ -719,6 +719,9 @@ static void phylink_validate_one(struct
+       tmp_state = *state;
+       tmp_state.interface = interface;
++      if (phy)
++              tmp_state.rate_matching = phy_get_rate_matching(phy, interface);
++
+       if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) {
+               phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n",
+                           interface, phy_modes(interface),
+@@ -740,7 +743,7 @@ static int phylink_validate_mask(struct
+       int interface;
+       for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
+-              phylink_validate_one(pl, supported, state, interface,
++              phylink_validate_one(pl, NULL, supported, state, interface,
+                                    all_s, all_adv);
+       linkmode_copy(supported, all_s);
diff --git a/target/linux/generic/backport-6.6/895-05-v6.8-net-phylink-pass-PHY-into-phylink_validate_mask.patch b/target/linux/generic/backport-6.6/895-05-v6.8-net-phylink-pass-PHY-into-phylink_validate_mask.patch
new file mode 100644 (file)
index 0000000..5f66869
--- /dev/null
@@ -0,0 +1,58 @@
+From c6fec66d3cd76d797f70b30f1511bed10ba45a96 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:29 +0000
+Subject: [PATCH 5/7] net: phylink: pass PHY into phylink_validate_mask()
+
+Pass the phy (if any) into phylink_validate_mask() so that we can
+validate each interface with its rate matching setting.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIL-00DDM3-HJ@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -734,7 +734,8 @@ static void phylink_validate_one(struct
+       }
+ }
+-static int phylink_validate_mask(struct phylink *pl, unsigned long *supported,
++static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy,
++                               unsigned long *supported,
+                                struct phylink_link_state *state,
+                                const unsigned long *interfaces)
+ {
+@@ -743,7 +744,7 @@ static int phylink_validate_mask(struct
+       int interface;
+       for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
+-              phylink_validate_one(pl, NULL, supported, state, interface,
++              phylink_validate_one(pl, phy, supported, state, interface,
+                                    all_s, all_adv);
+       linkmode_copy(supported, all_s);
+@@ -758,7 +759,8 @@ static int phylink_validate(struct phyli
+       const unsigned long *interfaces = pl->config->supported_interfaces;
+       if (state->interface == PHY_INTERFACE_MODE_NA)
+-              return phylink_validate_mask(pl, supported, state, interfaces);
++              return phylink_validate_mask(pl, NULL, supported, state,
++                                           interfaces);
+       if (!test_bit(state->interface, interfaces))
+               return -EINVAL;
+@@ -3194,7 +3196,8 @@ static int phylink_sfp_config_optical(st
+       /* For all the interfaces that are supported, reduce the sfp_support
+        * mask to only those link modes that can be supported.
+        */
+-      ret = phylink_validate_mask(pl, pl->sfp_support, &config, interfaces);
++      ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config,
++                                  interfaces);
+       if (ret) {
+               phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",
+                           __ETHTOOL_LINK_MODE_MASK_NBITS, support);
diff --git a/target/linux/generic/backport-6.6/895-06-v6.8-net-phylink-split-out-PHY-validation-from-phylink_br.patch b/target/linux/generic/backport-6.6/895-06-v6.8-net-phylink-split-out-PHY-validation-from-phylink_br.patch
new file mode 100644 (file)
index 0000000..e295033
--- /dev/null
@@ -0,0 +1,95 @@
+From ee0e0ddb910e7e989b65a19d72b6435baa641fc7 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:34 +0000
+Subject: [PATCH 6/7] net: phylink: split out PHY validation from
+ phylink_bringup_phy()
+
+When bringing up a PHY, we need to work out which ethtool link modes it
+should support and advertise. Clause 22 PHYs operate in a single
+interface mode, which can be easily dealt with. However, clause 45 PHYs
+tend to switch interface mode depending on the media. We need more
+flexible validation at this point, so this patch splits out that code
+in preparation to changing it.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIQ-00DDM9-LK@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 56 ++++++++++++++++++++++-----------------
+ 1 file changed, 31 insertions(+), 25 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1775,6 +1775,35 @@ static void phylink_phy_change(struct ph
+                   phylink_pause_to_str(pl->phy_state.pause));
+ }
++static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy,
++                              unsigned long *supported,
++                              struct phylink_link_state *state)
++{
++      /* Check whether we would use rate matching for the proposed interface
++       * mode.
++       */
++      state->rate_matching = phy_get_rate_matching(phy, state->interface);
++
++      /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R,
++       * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching.
++       * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching
++       * their Serdes is either unnecessary or not reasonable.
++       *
++       * For these which switch interface modes, we really need to know which
++       * interface modes the PHY supports to properly work out which ethtool
++       * linkmodes can be supported. For now, as a work-around, we validate
++       * against all interface modes, which may lead to more ethtool link
++       * modes being advertised than are actually supported.
++       */
++      if (phy->is_c45 && state->rate_matching == RATE_MATCH_NONE &&
++          state->interface != PHY_INTERFACE_MODE_RXAUI &&
++          state->interface != PHY_INTERFACE_MODE_XAUI &&
++          state->interface != PHY_INTERFACE_MODE_USXGMII)
++              state->interface = PHY_INTERFACE_MODE_NA;
++
++      return phylink_validate(pl, supported, state);
++}
++
+ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
+                              phy_interface_t interface)
+ {
+@@ -1795,32 +1824,9 @@ static int phylink_bringup_phy(struct ph
+       memset(&config, 0, sizeof(config));
+       linkmode_copy(supported, phy->supported);
+       linkmode_copy(config.advertising, phy->advertising);
++      config.interface = interface;
+-      /* Check whether we would use rate matching for the proposed interface
+-       * mode.
+-       */
+-      config.rate_matching = phy_get_rate_matching(phy, interface);
+-
+-      /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R,
+-       * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching.
+-       * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching
+-       * their Serdes is either unnecessary or not reasonable.
+-       *
+-       * For these which switch interface modes, we really need to know which
+-       * interface modes the PHY supports to properly work out which ethtool
+-       * linkmodes can be supported. For now, as a work-around, we validate
+-       * against all interface modes, which may lead to more ethtool link
+-       * modes being advertised than are actually supported.
+-       */
+-      if (phy->is_c45 && config.rate_matching == RATE_MATCH_NONE &&
+-          interface != PHY_INTERFACE_MODE_RXAUI &&
+-          interface != PHY_INTERFACE_MODE_XAUI &&
+-          interface != PHY_INTERFACE_MODE_USXGMII)
+-              config.interface = PHY_INTERFACE_MODE_NA;
+-      else
+-              config.interface = interface;
+-
+-      ret = phylink_validate(pl, supported, &config);
++      ret = phylink_validate_phy(pl, phy, supported, &config);
+       if (ret) {
+               phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %pe\n",
+                            phy_modes(config.interface),
diff --git a/target/linux/generic/backport-6.6/895-07-v6.8-net-phylink-use-the-PHY-s-possible_interfaces-if-pop.patch b/target/linux/generic/backport-6.6/895-07-v6.8-net-phylink-use-the-PHY-s-possible_interfaces-if-pop.patch
new file mode 100644 (file)
index 0000000..86ed7a8
--- /dev/null
@@ -0,0 +1,130 @@
+From 8f7a9799c5949f94ecc3acfd71b36437a7ade73b Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:39 +0000
+Subject: [PATCH 7/7] net: phylink: use the PHY's possible_interfaces if
+ populated
+
+Some PHYs such as Aquantia, Broadcom 84881, and Marvell 88X33x0 can
+switch between a set of interface types depending on the negotiated
+media speed, or can use rate adaption for some or all of these
+interface types.
+
+We currently assume that these are Clause 45 PHYs that are configured
+not to use a specific set of interface modes, which has worked so far,
+but is just a work-around. In this workaround, we validate using all
+interfaces that the MAC supports, which can lead to extra modes being
+advertised that can not be supported.
+
+To properly address this, switch to using the newly introduced PHY
+possible_interfaces bitmap which indicates which interface modes will
+be used by the PHY as configured. We calculate the union of the PHY's
+possible interfaces and MACs supported interfaces, checking that is
+non-empty. If the PHY is on a SFP, we further reduce the set by those
+which can be used on a SFP module, again checking that is non-empty.
+Finally, we validate the subset of interfaces, taking account of
+whether rate matching will be used for each individual interface mode.
+
+This becomes independent of whether the PHY is clause 22 or clause 45.
+
+It is encouraged that all PHYs that switch interface modes or use
+rate matching should populate phydev->possible_interfaces.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIV-00DDMF-Pi@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 67 +++++++++++++++++++++++++++++++--------
+ 1 file changed, 54 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -121,6 +121,19 @@ do {                                                                      \
+ })
+ #endif
++static const phy_interface_t phylink_sfp_interface_preference[] = {
++      PHY_INTERFACE_MODE_25GBASER,
++      PHY_INTERFACE_MODE_USXGMII,
++      PHY_INTERFACE_MODE_10GBASER,
++      PHY_INTERFACE_MODE_5GBASER,
++      PHY_INTERFACE_MODE_2500BASEX,
++      PHY_INTERFACE_MODE_SGMII,
++      PHY_INTERFACE_MODE_1000BASEX,
++      PHY_INTERFACE_MODE_100BASEX,
++};
++
++static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
++
+ /**
+  * phylink_set_port_modes() - set the port type modes in the ethtool mask
+  * @mask: ethtool link mode mask
+@@ -1779,6 +1792,47 @@ static int phylink_validate_phy(struct p
+                               unsigned long *supported,
+                               struct phylink_link_state *state)
+ {
++      DECLARE_PHY_INTERFACE_MASK(interfaces);
++
++      /* If the PHY provides a bitmap of the interfaces it will be using
++       * depending on the negotiated media speeds, use this to validate
++       * which ethtool link modes can be used.
++       */
++      if (!phy_interface_empty(phy->possible_interfaces)) {
++              /* We only care about the union of the PHY's interfaces and
++               * those which the host supports.
++               */
++              phy_interface_and(interfaces, phy->possible_interfaces,
++                                pl->config->supported_interfaces);
++
++              if (phy_interface_empty(interfaces)) {
++                      phylink_err(pl, "PHY has no common interfaces\n");
++                      return -EINVAL;
++              }
++
++              if (phy_on_sfp(phy)) {
++                      /* If the PHY is on a SFP, limit the interfaces to
++                       * those that can be used with a SFP module.
++                       */
++                      phy_interface_and(interfaces, interfaces,
++                                        phylink_sfp_interfaces);
++
++                      if (phy_interface_empty(interfaces)) {
++                              phylink_err(pl, "SFP PHY's possible interfaces becomes empty\n");
++                              return -EINVAL;
++                      }
++              }
++
++              phylink_dbg(pl, "PHY %s uses interfaces %*pbl, validating %*pbl\n",
++                          phydev_name(phy),
++                          (int)PHY_INTERFACE_MODE_MAX,
++                          phy->possible_interfaces,
++                          (int)PHY_INTERFACE_MODE_MAX, interfaces);
++
++              return phylink_validate_mask(pl, phy, supported, state,
++                                           interfaces);
++      }
++
+       /* Check whether we would use rate matching for the proposed interface
+        * mode.
+        */
+@@ -3047,19 +3101,6 @@ static void phylink_sfp_detach(void *ups
+       pl->netdev->sfp_bus = NULL;
+ }
+-static const phy_interface_t phylink_sfp_interface_preference[] = {
+-      PHY_INTERFACE_MODE_25GBASER,
+-      PHY_INTERFACE_MODE_USXGMII,
+-      PHY_INTERFACE_MODE_10GBASER,
+-      PHY_INTERFACE_MODE_5GBASER,
+-      PHY_INTERFACE_MODE_2500BASEX,
+-      PHY_INTERFACE_MODE_SGMII,
+-      PHY_INTERFACE_MODE_1000BASEX,
+-      PHY_INTERFACE_MODE_100BASEX,
+-};
+-
+-static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
+-
+ static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl,
+                                                   const unsigned long *intf)
+ {
diff --git a/target/linux/generic/backport-6.6/896-01-v6.9-net-dsa-mv88e6xxx-rename-mv88e6xxx_g2_scratch_gpio_s.patch b/target/linux/generic/backport-6.6/896-01-v6.9-net-dsa-mv88e6xxx-rename-mv88e6xxx_g2_scratch_gpio_s.patch
new file mode 100644 (file)
index 0000000..48dd6ff
--- /dev/null
@@ -0,0 +1,61 @@
+From 5c5b0c444be3e851046f1c1074459b8d15d2a0f9 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Tue, 27 Feb 2024 18:54:21 +0100
+Subject: [PATCH 1/2] net: dsa: mv88e6xxx: rename
+ mv88e6xxx_g2_scratch_gpio_set_smi
+
+The name mv88e6xxx_g2_scratch_gpio_set_smi is a bit ambiguous as it appears
+to only be applicable to the 6390 family, so lets rename it to
+mv88e6390_g2_scratch_gpio_set_smi to make it more obvious.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mv88e6xxx/chip.c            | 2 +-
+ drivers/net/dsa/mv88e6xxx/global2.h         | 2 +-
+ drivers/net/dsa/mv88e6xxx/global2_scratch.c | 4 ++--
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -3669,7 +3669,7 @@ static int mv88e6xxx_mdio_register(struc
+       if (external) {
+               mv88e6xxx_reg_lock(chip);
+-              err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true);
++              err = mv88e6390_g2_scratch_gpio_set_smi(chip, true);
+               mv88e6xxx_reg_unlock(chip);
+               if (err)
+--- a/drivers/net/dsa/mv88e6xxx/global2.h
++++ b/drivers/net/dsa/mv88e6xxx/global2.h
+@@ -378,7 +378,7 @@ extern const struct mv88e6xxx_avb_ops mv
+ extern const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops;
+-int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
++int mv88e6390_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);
+--- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c
++++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
+@@ -240,7 +240,7 @@ const struct mv88e6xxx_gpio_ops mv88e635
+ };
+ /**
+- * mv88e6xxx_g2_scratch_gpio_set_smi - set gpio muxing for external smi
++ * mv88e6390_g2_scratch_gpio_set_smi - set gpio muxing for external smi
+  * @chip: chip private data
+  * @external: set mux for external smi, or free for gpio usage
+  *
+@@ -248,7 +248,7 @@ const struct mv88e6xxx_gpio_ops mv88e635
+  * an external SMI interface, or they may be made free for other
+  * GPIO uses.
+  */
+-int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
++int mv88e6390_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
+                                     bool external)
+ {
+       int misc_cfg = MV88E6352_G2_SCRATCH_MISC_CFG;
diff --git a/target/linux/generic/backport-6.6/896-02-v6.9-net-dsa-mv88e6xxx-add-Amethyst-specific-SMI-GPIO-fun.patch b/target/linux/generic/backport-6.6/896-02-v6.9-net-dsa-mv88e6xxx-add-Amethyst-specific-SMI-GPIO-fun.patch
new file mode 100644 (file)
index 0000000..e593157
--- /dev/null
@@ -0,0 +1,92 @@
+From e3ab3267a0bbedc37725bb845a332ec33b247263 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Tue, 27 Feb 2024 18:54:22 +0100
+Subject: [PATCH 2/2] net: dsa: mv88e6xxx: add Amethyst specific SMI GPIO
+ function
+
+The existing mv88e6390_g2_scratch_gpio_set_smi() cannot be used on the
+88E6393X as it requires certain P0_MODE, it also checks the CPU mode
+as it impacts the bit setting value.
+
+This is all irrelevant for Amethyst (MV88E6191X/6193X/6393X) as only
+the default value of the SMI_PHY Config bit is set to CPU_MGD bootstrap
+pin value but it can be changed without restrictions so that GPIO pins
+9 and 10 are used as SMI pins.
+
+So, introduce Amethyst specific function and call that if the Amethyst
+family wants to setup the external PHY.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mv88e6xxx/chip.c            |  5 +++-
+ drivers/net/dsa/mv88e6xxx/global2.h         |  2 ++
+ drivers/net/dsa/mv88e6xxx/global2_scratch.c | 31 +++++++++++++++++++++
+ 3 files changed, 37 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -3669,7 +3669,10 @@ static int mv88e6xxx_mdio_register(struc
+       if (external) {
+               mv88e6xxx_reg_lock(chip);
+-              err = mv88e6390_g2_scratch_gpio_set_smi(chip, true);
++              if (chip->info->family == MV88E6XXX_FAMILY_6393)
++                      err = mv88e6393x_g2_scratch_gpio_set_smi(chip, true);
++              else
++                      err = mv88e6390_g2_scratch_gpio_set_smi(chip, true);
+               mv88e6xxx_reg_unlock(chip);
+               if (err)
+--- a/drivers/net/dsa/mv88e6xxx/global2.h
++++ b/drivers/net/dsa/mv88e6xxx/global2.h
+@@ -380,6 +380,8 @@ extern const struct mv88e6xxx_gpio_ops m
+ int mv88e6390_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
+                                     bool external);
++int mv88e6393x_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);
+--- a/drivers/net/dsa/mv88e6xxx/global2_scratch.c
++++ b/drivers/net/dsa/mv88e6xxx/global2_scratch.c
+@@ -291,6 +291,37 @@ int mv88e6390_g2_scratch_gpio_set_smi(st
+ }
+ /**
++ * mv88e6393x_g2_scratch_gpio_set_smi - set gpio muxing for external smi
++ * @chip: chip private data
++ * @external: set mux for external smi, or free for gpio usage
++ *
++ * MV88E6191X/6193X/6393X GPIO pins 9 and 10 can be configured as an
++ * external SMI interface or as regular GPIO-s.
++ *
++ * They however have a different register layout then the existing
++ * function.
++ */
++
++int mv88e6393x_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
++                                     bool external)
++{
++      int misc_cfg = MV88E6352_G2_SCRATCH_MISC_CFG;
++      int err;
++      u8 val;
++
++      err = mv88e6xxx_g2_scratch_read(chip, misc_cfg, &val);
++      if (err)
++              return err;
++
++      if (external)
++              val &= ~MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI;
++      else
++              val |= MV88E6352_G2_SCRATCH_MISC_CFG_NORMALSMI;
++
++      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
diff --git a/target/linux/generic/backport-6.6/897-01-v6.9-net-phy-qcom-qca808x-add-helper-for-checking-for-1G-.patch b/target/linux/generic/backport-6.6/897-01-v6.9-net-phy-qcom-qca808x-add-helper-for-checking-for-1G-.patch
new file mode 100644 (file)
index 0000000..a11e804
--- /dev/null
@@ -0,0 +1,50 @@
+From f058b2dd70b1a5503dff899010aeb53b436091e5 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 28 Feb 2024 18:24:09 +0100
+Subject: [PATCH 1/2] net: phy: qcom: qca808x: add helper for checking for 1G
+ only model
+
+There are 2 versions of QCA808x, one 2.5G capable and one 1G capable.
+Currently, this matter only in the .get_features call however, it will
+be required for filling supported interface modes so lets add a helper
+that can be reused.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 17 ++++++++++++-----
+ 1 file changed, 12 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -156,6 +156,17 @@ static bool qca808x_has_fast_retrain_or_
+       return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+ }
++static bool qca808x_is_1g_only(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
++      if (ret < 0)
++              return true;
++
++      return !!(QCA808X_PHY_CHIP_TYPE_1G & ret);
++}
++
+ static int qca808x_probe(struct phy_device *phydev)
+ {
+       struct device *dev = &phydev->mdio.dev;
+@@ -350,11 +361,7 @@ static int qca808x_get_features(struct p
+        * existed in the bit0 of MMD1.21, we need to remove it manually if
+        * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
+        */
+-      ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
+-      if (ret < 0)
+-              return ret;
+-
+-      if (QCA808X_PHY_CHIP_TYPE_1G & ret)
++      if (qca808x_is_1g_only(phydev))
+               linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+       return 0;
diff --git a/target/linux/generic/backport-6.6/897-02-v6.9-net-phy-qcom-qca808x-fill-in-possible_interfaces.patch b/target/linux/generic/backport-6.6/897-02-v6.9-net-phy-qcom-qca808x-fill-in-possible_interfaces.patch
new file mode 100644 (file)
index 0000000..c162fc7
--- /dev/null
@@ -0,0 +1,44 @@
+From cb28f702960695e26597c332b0e46776e825cc34 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 28 Feb 2024 18:24:10 +0100
+Subject: [PATCH 2/2] net: phy: qcom: qca808x: fill in possible_interfaces
+
+Currently QCA808x driver does not fill the possible_interfaces.
+2.5G QCA808x support SGMII and 2500Base-X while 1G model only supports
+SGMII, so fill the possible_interfaces accordingly.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -167,6 +167,16 @@ static bool qca808x_is_1g_only(struct ph
+       return !!(QCA808X_PHY_CHIP_TYPE_1G & ret);
+ }
++static void qca808x_fill_possible_interfaces(struct phy_device *phydev)
++{
++      unsigned long *possible = phydev->possible_interfaces;
++
++      __set_bit(PHY_INTERFACE_MODE_SGMII, possible);
++
++      if (!qca808x_is_1g_only(phydev))
++              __set_bit(PHY_INTERFACE_MODE_2500BASEX, possible);
++}
++
+ static int qca808x_probe(struct phy_device *phydev)
+ {
+       struct device *dev = &phydev->mdio.dev;
+@@ -231,6 +241,8 @@ static int qca808x_config_init(struct ph
+               }
+       }
++      qca808x_fill_possible_interfaces(phydev);
++
+       /* Configure adc threshold as 100mv for the link 10M */
+       return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
+                                    QCA808X_ADC_THRESHOLD_MASK,
diff --git a/target/linux/generic/backport-6.6/898-v6.7-mtd-spinand-winbond-add-support-for-serial-NAND-flash.patch b/target/linux/generic/backport-6.6/898-v6.7-mtd-spinand-winbond-add-support-for-serial-NAND-flash.patch
new file mode 100644 (file)
index 0000000..b5aeaef
--- /dev/null
@@ -0,0 +1,75 @@
+From 6a804fb72de56d6a99b799f565ae45f2cec7cd55 Mon Sep 17 00:00:00 2001
+From: Sridharan S N <quic_sridsn@quicinc.com>
+Date: Thu, 12 Oct 2023 12:11:34 +0530
+Subject: mtd: spinand: winbond: add support for serial NAND flash
+
+Add support for W25N01JW, W25N02JWZEIF, W25N512GW,
+W25N02KWZEIR and W25N01GWZEIG.
+
+W25N02KWZEIR has 8b/512b on-die ECC capability and other
+four has 4b/512b on-die ECC capability.
+
+Signed-off-by: Sridharan S N <quic_sridsn@quicinc.com>
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20231012064134.4068621-1-quic_sridsn@quicinc.com
+---
+ drivers/mtd/nand/spi/winbond.c | 45 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 45 insertions(+)
+
+(limited to 'drivers/mtd/nand/spi/winbond.c')
+
+--- a/drivers/mtd/nand/spi/winbond.c
++++ b/drivers/mtd/nand/spi/winbond.c
+@@ -169,6 +169,51 @@ static const struct spinand_info winbond
+                                             &update_cache_variants),
+                    0,
+                    SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
++      SPINAND_INFO("W25N01JW",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbc, 0x21),
++                   NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25m02gv_ooblayout, w25n02kv_ecc_get_status)),
++      SPINAND_INFO("W25N02JWZEIF",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbf, 0x22),
++                   NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 2, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
++      SPINAND_INFO("W25N512GW",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x20),
++                   NAND_MEMORG(1, 2048, 64, 64, 512, 10, 1, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
++      SPINAND_INFO("W25N02KWZEIR",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x22),
++                   NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
++      SPINAND_INFO("W25N01GWZEIG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xba, 0x21),
++                   NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25m02gv_ooblayout, w25n02kv_ecc_get_status)),
+ };
+ static int winbond_spinand_init(struct spinand_device *spinand)
diff --git a/target/linux/generic/backport-6.6/899-v6.9-mtd-spinand-winbond-add-W25N04KV.patch b/target/linux/generic/backport-6.6/899-v6.9-mtd-spinand-winbond-add-W25N04KV.patch
new file mode 100644 (file)
index 0000000..3e5d918
--- /dev/null
@@ -0,0 +1,53 @@
+From e0ccf861b80698a5cc6f97c89bf8d5761f465fce Mon Sep 17 00:00:00 2001
+From: Zhi-Jun You <hujy652@gmail.com>
+Date: Sun, 7 Jan 2024 14:41:20 +0000
+Subject: mtd: spinand: winbond: add support for W25N04KV
+
+Add support for W25N04KV.
+
+W25N04KV has 8-bit on-die ECC.
+
+Signed-off-by: Zhi-Jun You <hujy652@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://lore.kernel.org/linux-mtd/20240107144120.532-1-hujy652@gmail.com
+---
+ drivers/mtd/nand/spi/winbond.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+(limited to 'drivers/mtd/nand/spi/winbond.c')
+
+--- a/drivers/mtd/nand/spi/winbond.c
++++ b/drivers/mtd/nand/spi/winbond.c
+@@ -15,6 +15,8 @@
+ #define WINBOND_CFG_BUF_READ          BIT(3)
++#define W25N04KV_STATUS_ECC_5_8_BITFLIPS      (3 << 4)
++
+ 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),
+@@ -118,6 +120,7 @@ static int w25n02kv_ecc_get_status(struc
+               return -EBADMSG;
+       case STATUS_ECC_HAS_BITFLIPS:
++      case W25N04KV_STATUS_ECC_5_8_BITFLIPS:
+               /*
+                * Let's try to retrieve the real maximum number of bitflips
+                * in order to avoid forcing the wear-leveling layer to move
+@@ -214,6 +217,15 @@ static const struct spinand_info winbond
+                                             &update_cache_variants),
+                    0,
+                    SPINAND_ECCINFO(&w25m02gv_ooblayout, w25n02kv_ecc_get_status)),
++      SPINAND_INFO("W25N04KV",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
++                   NAND_MEMORG(1, 2048, 128, 64, 4096, 40, 2, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
+ };
+ static int winbond_spinand_init(struct spinand_device *spinand)
diff --git a/target/linux/generic/backport-6.6/900-v6.11-net-free_netdev-exit-earlier-if-dummy.patch b/target/linux/generic/backport-6.6/900-v6.11-net-free_netdev-exit-earlier-if-dummy.patch
new file mode 100644 (file)
index 0000000..21db874
--- /dev/null
@@ -0,0 +1,35 @@
+From f8d05679fb3faae478d604177b0c188b340371cd Mon Sep 17 00:00:00 2001
+From: Breno Leitao <leitao@debian.org>
+Date: Mon, 22 Apr 2024 05:38:55 -0700
+Subject: [PATCH] net: free_netdev: exit earlier if dummy
+
+For dummy devices, exit earlier at free_netdev() instead of executing
+the whole function. This is necessary, because dummy devices are
+special, and shouldn't have the second part of the function executed.
+
+Otherwise reg_state, which is NETREG_DUMMY, will be overwritten and
+there will be no way to identify that this is a dummy device. Also, this
+device do not need the final put_device(), since dummy devices are not
+registered (through register_netdevice()), where the device reference is
+increased (at netdev_register_kobject()/device_add()).
+
+Suggested-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Breno Leitao <leitao@debian.org>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ net/core/dev.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -10983,7 +10983,8 @@ void free_netdev(struct net_device *dev)
+       dev->xdp_bulkq = NULL;
+       /*  Compatibility with error handling in drivers */
+-      if (dev->reg_state == NETREG_UNINITIALIZED) {
++      if (dev->reg_state == NETREG_UNINITIALIZED ||
++          dev->reg_state == NETREG_DUMMY) {
+               netdev_freemem(dev);
+               return;
+       }
diff --git a/target/linux/generic/backport-6.6/901-v6.13-net-dsa-mv88e6xxx-Support-LED-control.patch b/target/linux/generic/backport-6.6/901-v6.13-net-dsa-mv88e6xxx-Support-LED-control.patch
new file mode 100644 (file)
index 0000000..711246d
--- /dev/null
@@ -0,0 +1,1250 @@
+From 7b590490e3aa6bfa38bf6e2069a529017fd3c1d2 Mon Sep 17 00:00:00 2001
+From: Linus Walleij <linus.walleij@linaro.org>
+Date: Fri, 13 Oct 2023 00:08:35 +0200
+Subject: [PATCH] net: dsa: mv88e6xxx: Support LED control
+
+This adds control over the hardware LEDs in the Marvell
+MV88E6xxx DSA switch and enables it for MV88E6352.
+
+This fixes an imminent problem on the Inteno XG6846 which
+has a WAN LED that simply do not work with hardware
+defaults: driver amendment is necessary.
+
+The patch is modeled after Christian Marangis LED support
+code for the QCA8k DSA switch, I got help with the register
+definitions from Tim Harvey.
+
+After this patch it is possible to activate hardware link
+indication like this (or with a similar script):
+
+  cd /sys/class/leds/Marvell\ 88E6352:05:00:green:wan/
+  echo netdev > trigger
+  echo 1 > link
+
+This makes the green link indicator come up on any link
+speed. It is also possible to be more elaborate, like this:
+
+  cd /sys/class/leds/Marvell\ 88E6352:05:00:green:wan/
+  echo netdev > trigger
+  echo 1 > link_1000
+  cd /sys/class/leds/Marvell\ 88E6352:05:01:amber:wan/
+  echo netdev > trigger
+  echo 1 > link_100
+
+Making the green LED come on for a gigabit link and the
+amber LED come on for a 100 mbit link.
+
+Each port has 2 LED slots (the hardware may use just one or
+none) and the hardware triggers are specified in four bits per
+LED, and some of the hardware triggers are only available on the
+SFP (fiber) uplink. The restrictions are described in the
+port.h header file where the registers are described. For
+example, selector 1 set for LED 1 on port 5 or 6 will indicate
+Fiber 1000 (gigabit) and activity with a blinking LED, but
+ONLY for an SFP connection. If port 5/6 is used with something
+not SFP, this selector is a noop: something else need to be
+selected.
+
+After the previous series rewriting the MV88E6xxx DT
+bindings to use YAML a "leds" subnode is already valid
+for each port, in my scratch device tree it looks like
+this:
+
+   leds {
+     #address-cells = <1>;
+     #size-cells = <0>;
+
+     led@0 {
+       reg = <0>;
+       color = <LED_COLOR_ID_GREEN>;
+       function = LED_FUNCTION_LAN;
+       default-state = "off";
+       linux,default-trigger = "netdev";
+     };
+     led@1 {
+       reg = <1>;
+       color = <LED_COLOR_ID_AMBER>;
+       function = LED_FUNCTION_LAN;
+       default-state = "off";
+     };
+   };
+
+This DT config is not yet configuring everything: when the netdev
+default trigger is assigned the hw acceleration callbacks are
+not called, and there is no way to set the netdev sub-trigger
+type (such as link_1000) from the device tree, such as if you want
+a gigabit link indicator. This has to be done from userspace at
+this point.
+
+We add LED operations to all switches in the 6352 family:
+6172, 6176, 6240 and 6352.
+
+Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
+---
+ drivers/net/dsa/mv88e6xxx/Kconfig  |  10 +
+ drivers/net/dsa/mv88e6xxx/Makefile |   1 +
+ drivers/net/dsa/mv88e6xxx/chip.c   |  38 +-
+ drivers/net/dsa/mv88e6xxx/chip.h   |  11 +
+ drivers/net/dsa/mv88e6xxx/leds.c   | 839 +++++++++++++++++++++++++++++
+ drivers/net/dsa/mv88e6xxx/port.c   |   1 +
+ drivers/net/dsa/mv88e6xxx/port.h   | 133 +++++
+ 7 files changed, 1031 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/net/dsa/mv88e6xxx/leds.c
+
+--- a/drivers/net/dsa/mv88e6xxx/Kconfig
++++ b/drivers/net/dsa/mv88e6xxx/Kconfig
+@@ -17,3 +17,13 @@ config NET_DSA_MV88E6XXX_PTP
+       help
+         Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch
+         chips that support it.
++
++config NET_DSA_MV88E6XXX_LEDS
++      bool "LED support for Marvell 88E6xxx"
++      default y
++      depends on NET_DSA_MV88E6XXX
++      depends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_MV88E6XXX
++      depends on LEDS_TRIGGERS
++      help
++        This enabled support for controlling the LEDs attached to the
++        Marvell 88E6xxx switch chips.
+--- a/drivers/net/dsa/mv88e6xxx/Makefile
++++ b/drivers/net/dsa/mv88e6xxx/Makefile
+@@ -9,6 +9,7 @@ mv88e6xxx-objs += global2.o
+ mv88e6xxx-objs += global2_avb.o
+ mv88e6xxx-objs += global2_scratch.o
+ mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o
++mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_LEDS) += leds.o
+ mv88e6xxx-objs += pcs-6185.o
+ mv88e6xxx-objs += pcs-6352.o
+ mv88e6xxx-objs += pcs-639x.o
+--- a/drivers/net/dsa/mv88e6xxx/chip.c
++++ b/drivers/net/dsa/mv88e6xxx/chip.c
+@@ -27,6 +27,7 @@
+ #include <linux/of_irq.h>
+ #include <linux/of_mdio.h>
+ #include <linux/platform_data/mv88e6xxx.h>
++#include <linux/property.h>
+ #include <linux/netdevice.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/phylink.h>
+@@ -3235,14 +3236,43 @@ static int mv88e6xxx_setup_upstream_port
+ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
+ {
+       struct device_node *phy_handle = NULL;
++      struct fwnode_handle *ports_fwnode;
++      struct fwnode_handle *port_fwnode;
+       struct dsa_switch *ds = chip->ds;
++      struct mv88e6xxx_port *p;
+       struct dsa_port *dp;
+       int tx_amp;
+       int err;
+       u16 reg;
++      u32 val;
+-      chip->ports[port].chip = chip;
+-      chip->ports[port].port = port;
++      p = &chip->ports[port];
++      p->chip = chip;
++      p->port = port;
++
++      /* Look up corresponding fwnode if any */
++      ports_fwnode = device_get_named_child_node(chip->dev, "ethernet-ports");
++      if (!ports_fwnode)
++              ports_fwnode = device_get_named_child_node(chip->dev, "ports");
++      if (ports_fwnode) {
++              fwnode_for_each_child_node(ports_fwnode, port_fwnode) {
++                      if (fwnode_property_read_u32(port_fwnode, "reg", &val))
++                              continue;
++                      if (val == port) {
++                              p->fwnode = port_fwnode;
++                              p->fiber = fwnode_property_present(port_fwnode, "sfp");
++                              break;
++                      }
++              }
++      } else {
++              dev_dbg(chip->dev, "no ethernet ports node defined for the device\n");
++      }
++
++      if (chip->info->ops->port_setup_leds) {
++              err = chip->info->ops->port_setup_leds(chip, port);
++              if (err && err != -EOPNOTSUPP)
++                      return err;
++      }
+       err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED,
+                                      SPEED_UNFORCED, DUPLEX_UNFORCED,
+@@ -4461,6 +4491,7 @@ static const struct mv88e6xxx_ops mv88e6
+       .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
+       .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
+       .port_get_cmode = mv88e6352_port_get_cmode,
++      .port_setup_leds = mv88e6xxx_port_setup_leds,
+       .port_setup_message_port = mv88e6xxx_setup_message_port,
+       .stats_snapshot = mv88e6320_g1_stats_snapshot,
+       .stats_set_histogram = mv88e6095_g1_stats_set_histogram,
+@@ -4563,6 +4594,7 @@ static const struct mv88e6xxx_ops mv88e6
+       .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
+       .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
+       .port_get_cmode = mv88e6352_port_get_cmode,
++      .port_setup_leds = mv88e6xxx_port_setup_leds,
+       .port_setup_message_port = mv88e6xxx_setup_message_port,
+       .stats_snapshot = mv88e6320_g1_stats_snapshot,
+       .stats_set_histogram = mv88e6095_g1_stats_set_histogram,
+@@ -4838,6 +4870,7 @@ static const struct mv88e6xxx_ops mv88e6
+       .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
+       .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
+       .port_get_cmode = mv88e6352_port_get_cmode,
++      .port_setup_leds = mv88e6xxx_port_setup_leds,
+       .port_setup_message_port = mv88e6xxx_setup_message_port,
+       .stats_snapshot = mv88e6320_g1_stats_snapshot,
+       .stats_set_histogram = mv88e6095_g1_stats_set_histogram,
+@@ -5260,6 +5293,7 @@ static const struct mv88e6xxx_ops mv88e6
+       .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
+       .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
+       .port_get_cmode = mv88e6352_port_get_cmode,
++      .port_setup_leds = mv88e6xxx_port_setup_leds,
+       .port_setup_message_port = mv88e6xxx_setup_message_port,
+       .stats_snapshot = mv88e6320_g1_stats_snapshot,
+       .stats_set_histogram = mv88e6095_g1_stats_set_histogram,
+--- a/drivers/net/dsa/mv88e6xxx/chip.h
++++ b/drivers/net/dsa/mv88e6xxx/chip.h
+@@ -13,7 +13,9 @@
+ #include <linux/irq.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/kthread.h>
++#include <linux/leds.h>
+ #include <linux/phy.h>
++#include <linux/property.h>
+ #include <linux/ptp_clock_kernel.h>
+ #include <linux/timecounter.h>
+ #include <net/dsa.h>
+@@ -276,6 +278,7 @@ struct mv88e6xxx_vlan {
+ struct mv88e6xxx_port {
+       struct mv88e6xxx_chip *chip;
+       int port;
++      struct fwnode_handle *fwnode;
+       struct mv88e6xxx_vlan bridge_pvid;
+       u64 serdes_stats[2];
+       u64 atu_member_violation;
+@@ -290,6 +293,11 @@ struct mv88e6xxx_port {
+       struct devlink_region *region;
+       void *pcs_private;
++      /* LED related information */
++      bool fiber;
++      struct led_classdev led0;
++      struct led_classdev led1;
++
+       /* MacAuth Bypass control flag */
+       bool mab;
+ };
+@@ -563,6 +571,9 @@ struct mv88e6xxx_ops {
+                             phy_interface_t mode);
+       int (*port_get_cmode)(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
++      /* LED control */
++      int (*port_setup_leds)(struct mv88e6xxx_chip *chip, int port);
++
+       /* Some devices have a per port register indicating what is
+        * the upstream port this port should forward to.
+        */
+--- /dev/null
++++ b/drivers/net/dsa/mv88e6xxx/leds.c
+@@ -0,0 +1,839 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++#include <linux/bitfield.h>
++#include <linux/leds.h>
++#include <linux/property.h>
++
++#include "chip.h"
++#include "global2.h"
++#include "port.h"
++
++/* Offset 0x16: LED control */
++
++static int mv88e6xxx_port_led_write(struct mv88e6xxx_chip *chip, int port, u16 reg)
++{
++      reg |= MV88E6XXX_PORT_LED_CONTROL_UPDATE;
++
++      return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_LED_CONTROL, reg);
++}
++
++static int mv88e6xxx_port_led_read(struct mv88e6xxx_chip *chip, int port,
++                                 u16 ptr, u16 *val)
++{
++      int err;
++
++      err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_LED_CONTROL, ptr);
++      if (err)
++              return err;
++
++      err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_LED_CONTROL, val);
++      *val &= 0x3ff;
++
++      return err;
++}
++
++static int mv88e6xxx_led_brightness_set(struct mv88e6xxx_port *p, int led,
++                                      int brightness)
++{
++      u16 reg;
++      int err;
++
++      err = mv88e6xxx_port_led_read(p->chip, p->port,
++                                    MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL,
++                                    &reg);
++      if (err)
++              return err;
++
++      if (led == 1)
++              reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
++      else
++              reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
++
++      if (brightness) {
++              /* Selector 0x0f == Force LED ON */
++              if (led == 1)
++                      reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELF;
++              else
++                      reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELF;
++      } else {
++              /* Selector 0x0e == Force LED OFF */
++              if (led == 1)
++                      reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELE;
++              else
++                      reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELE;
++      }
++
++      reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL;
++
++      return mv88e6xxx_port_led_write(p->chip, p->port, reg);
++}
++
++static int mv88e6xxx_led0_brightness_set_blocking(struct led_classdev *ldev,
++                                                enum led_brightness brightness)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
++      int err;
++
++      mv88e6xxx_reg_lock(p->chip);
++      err = mv88e6xxx_led_brightness_set(p, 0, brightness);
++      mv88e6xxx_reg_unlock(p->chip);
++
++      return err;
++}
++
++static int mv88e6xxx_led1_brightness_set_blocking(struct led_classdev *ldev,
++                                                enum led_brightness brightness)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
++      int err;
++
++      mv88e6xxx_reg_lock(p->chip);
++      err = mv88e6xxx_led_brightness_set(p, 1, brightness);
++      mv88e6xxx_reg_unlock(p->chip);
++
++      return err;
++}
++
++struct mv88e6xxx_led_hwconfig {
++      int led;
++      u8 portmask;
++      unsigned long rules;
++      bool fiber;
++      bool blink_activity;
++      u16 selector;
++};
++
++/* The following is a lookup table to check what rules we can support on a
++ * certain LED given restrictions such as that some rules only work with fiber
++ * (SFP) connections and some blink on activity by default.
++ */
++#define MV88E6XXX_PORTS_0_3 (BIT(0) | BIT(1) | BIT(2) | BIT(3))
++#define MV88E6XXX_PORTS_4_5 (BIT(4) | BIT(5))
++#define MV88E6XXX_PORT_4 BIT(4)
++#define MV88E6XXX_PORT_5 BIT(5)
++
++/* Entries are listed in selector order.
++ *
++ * These configurations vary across different switch families, list
++ * different tables per-family here.
++ */
++static const struct mv88e6xxx_led_hwconfig mv88e6352_led_hwconfigs[] = {
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORT_4,
++              .rules = BIT(TRIGGER_NETDEV_LINK),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL0,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORT_5,
++              .rules = BIT(TRIGGER_NETDEV_LINK_1000),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL0,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_4_5,
++              .rules = BIT(TRIGGER_NETDEV_LINK_100),
++              .blink_activity = true,
++              .fiber = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_4_5,
++              .rules = BIT(TRIGGER_NETDEV_LINK_1000),
++              .blink_activity = true,
++              .fiber = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_1000),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_4_5,
++              .rules = BIT(TRIGGER_NETDEV_LINK_1000),
++              .blink_activity = true,
++              .fiber = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_4_5,
++              .rules = BIT(TRIGGER_NETDEV_LINK_100),
++              .blink_activity = true,
++              .fiber = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL3,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_1000),
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_4_5,
++              .rules = BIT(TRIGGER_NETDEV_LINK),
++              .fiber = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORT_4,
++              .rules = BIT(TRIGGER_NETDEV_LINK),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL4,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORT_5,
++              .rules = BIT(TRIGGER_NETDEV_LINK),
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL5,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORT_4,
++              .rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORT_5,
++              .rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL7,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000),
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL7,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK),
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL8,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORT_5,
++              .rules = BIT(TRIGGER_NETDEV_LINK),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_10),
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL9,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_100),
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL9,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_10),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SELA,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_100),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SELA,
++      },
++      {
++              .led = 0,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000),
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SELB,
++      },
++      {
++              .led = 1,
++              .portmask = MV88E6XXX_PORTS_0_3,
++              .rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000),
++              .blink_activity = true,
++              .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SELB,
++      },
++};
++
++/* mv88e6xxx_led_match_selector() - look up the appropriate LED mode selector
++ * @p: port state container
++ * @led: LED number, 0 or 1
++ * @blink_activity: blink the LED (usually blink on indicated activity)
++ * @fiber: the link is connected to fiber such as SFP
++ * @rules: LED status flags from the LED classdev core
++ * @selector: fill in the selector in this parameter with an OR operation
++ */
++static int mv88e6xxx_led_match_selector(struct mv88e6xxx_port *p, int led, bool blink_activity,
++                                      bool fiber, unsigned long rules, u16 *selector)
++{
++      const struct mv88e6xxx_led_hwconfig *conf;
++      int i;
++
++      /* No rules means we turn the LED off */
++      if (!rules) {
++              if (led == 1)
++                      *selector |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELE;
++              else
++                      *selector |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELE;
++              return 0;
++      }
++
++      /* TODO: these rules are for MV88E6352, when adding other families,
++       * think about making sure you select the table that match the
++       * specific switch family.
++       */
++      for (i = 0; i < ARRAY_SIZE(mv88e6352_led_hwconfigs); i++) {
++              conf = &mv88e6352_led_hwconfigs[i];
++
++              if (conf->led != led)
++                      continue;
++
++              if (!(conf->portmask & BIT(p->port)))
++                      continue;
++
++              if (conf->blink_activity != blink_activity)
++                      continue;
++
++              if (conf->fiber != fiber)
++                      continue;
++
++              if (conf->rules == rules) {
++                      dev_dbg(p->chip->dev, "port%d LED %d set selector %04x for rules %08lx\n",
++                              p->port, led, conf->selector, rules);
++                      *selector |= conf->selector;
++                      return 0;
++              }
++      }
++
++      return -EOPNOTSUPP;
++}
++
++/* mv88e6xxx_led_match_selector() - find Linux netdev rules from a selector value
++ * @p: port state container
++ * @selector: the selector value from the LED actity register
++ * @led: LED number, 0 or 1
++ * @rules: Linux netdev activity rules found from selector
++ */
++static int
++mv88e6xxx_led_match_rule(struct mv88e6xxx_port *p, u16 selector, int led, unsigned long *rules)
++{
++      const struct mv88e6xxx_led_hwconfig *conf;
++      int i;
++
++      /* Find the selector in the table, we just look for the right selector
++       * and ignore if the activity has special properties such as blinking
++       * or is fiber-only.
++       */
++      for (i = 0; i < ARRAY_SIZE(mv88e6352_led_hwconfigs); i++) {
++              conf = &mv88e6352_led_hwconfigs[i];
++
++              if (conf->led != led)
++                      continue;
++
++              if (!(conf->portmask & BIT(p->port)))
++                      continue;
++
++              if (conf->selector == selector) {
++                      dev_dbg(p->chip->dev, "port%d LED %d has selector %04x, rules %08lx\n",
++                              p->port, led, selector, conf->rules);
++                      *rules = conf->rules;
++                      return 0;
++              }
++      }
++
++      return -EINVAL;
++}
++
++/* mv88e6xxx_led_get_selector() - get the appropriate LED mode selector
++ * @p: port state container
++ * @led: LED number, 0 or 1
++ * @fiber: the link is connected to fiber such as SFP
++ * @rules: LED status flags from the LED classdev core
++ * @selector: fill in the selector in this parameter with an OR operation
++ */
++static int mv88e6xxx_led_get_selector(struct mv88e6xxx_port *p, int led,
++                                    bool fiber, unsigned long rules, u16 *selector)
++{
++      int err;
++
++      /* What happens here is that we first try to locate a trigger with solid
++       * indicator (such as LED is on for a 1000 link) else we try a second
++       * sweep to find something suitable with a trigger that will blink on
++       * activity.
++       */
++      err = mv88e6xxx_led_match_selector(p, led, false, fiber, rules, selector);
++      if (err)
++              return mv88e6xxx_led_match_selector(p, led, true, fiber, rules, selector);
++
++      return 0;
++}
++
++/* Sets up the hardware blinking period */
++static int mv88e6xxx_led_set_blinking_period(struct mv88e6xxx_port *p, int led,
++                                           unsigned long delay_on, unsigned long delay_off)
++{
++      unsigned long period;
++      u16 reg;
++
++      period = delay_on + delay_off;
++
++      reg = 0;
++
++      switch (period) {
++      case 21:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_21MS;
++              break;
++      case 42:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_42MS;
++              break;
++      case 84:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_84MS;
++              break;
++      case 168:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_168MS;
++              break;
++      case 336:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_336MS;
++              break;
++      case 672:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_672MS;
++              break;
++      default:
++              /* Fall back to software blinking */
++              return -EINVAL;
++      }
++
++      /* This is essentially PWM duty cycle: how long time of the period
++       * will the LED be on. Zero isn't great in most cases.
++       */
++      switch (delay_on) {
++      case 0:
++              /* This is usually pretty useless and will make the LED look OFF */
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_NONE;
++              break;
++      case 21:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS;
++              break;
++      case 42:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_42MS;
++              break;
++      case 84:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_84MS;
++              break;
++      case 168:
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_168MS;
++              break;
++      default:
++              /* Just use something non-zero */
++              reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS;
++              break;
++      }
++
++      /* Set up blink rate */
++      reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_STRETCH_BLINK;
++
++      return mv88e6xxx_port_led_write(p->chip, p->port, reg);
++}
++
++static int mv88e6xxx_led_blink_set(struct mv88e6xxx_port *p, int led,
++                                 unsigned long *delay_on, unsigned long *delay_off)
++{
++      u16 reg;
++      int err;
++
++      /* Choose a sensible default 336 ms (~3 Hz) */
++      if ((*delay_on == 0) && (*delay_off == 0)) {
++              *delay_on = 168;
++              *delay_off = 168;
++      }
++
++      /* No off delay is just on */
++      if (*delay_off == 0)
++              return mv88e6xxx_led_brightness_set(p, led, 1);
++
++      err = mv88e6xxx_led_set_blinking_period(p, led, *delay_on, *delay_off);
++      if (err)
++              return err;
++
++      err = mv88e6xxx_port_led_read(p->chip, p->port,
++                                    MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL,
++                                    &reg);
++      if (err)
++              return err;
++
++      if (led == 1)
++              reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
++      else
++              reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
++
++      /* This will select the forced blinking status */
++      if (led == 1)
++              reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELD;
++      else
++              reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELD;
++
++      reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL;
++
++      return mv88e6xxx_port_led_write(p->chip, p->port, reg);
++}
++
++static int mv88e6xxx_led0_blink_set(struct led_classdev *ldev,
++                                  unsigned long *delay_on,
++                                  unsigned long *delay_off)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
++      int err;
++
++      mv88e6xxx_reg_lock(p->chip);
++      err = mv88e6xxx_led_blink_set(p, 0, delay_on, delay_off);
++      mv88e6xxx_reg_unlock(p->chip);
++
++      return err;
++}
++
++static int mv88e6xxx_led1_blink_set(struct led_classdev *ldev,
++                                  unsigned long *delay_on,
++                                  unsigned long *delay_off)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
++      int err;
++
++      mv88e6xxx_reg_lock(p->chip);
++      err = mv88e6xxx_led_blink_set(p, 1, delay_on, delay_off);
++      mv88e6xxx_reg_unlock(p->chip);
++
++      return err;
++}
++
++static int
++mv88e6xxx_led0_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
++      u16 selector = 0;
++
++      return mv88e6xxx_led_get_selector(p, 0, p->fiber, rules, &selector);
++}
++
++static int
++mv88e6xxx_led1_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
++      u16 selector = 0;
++
++      return mv88e6xxx_led_get_selector(p, 1, p->fiber, rules, &selector);
++}
++
++static int mv88e6xxx_led_hw_control_set(struct mv88e6xxx_port *p,
++                                      int led, unsigned long rules)
++{
++      u16 reg;
++      int err;
++
++      err = mv88e6xxx_port_led_read(p->chip, p->port,
++                                    MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL,
++                                    &reg);
++      if (err)
++              return err;
++
++      if (led == 1)
++              reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
++      else
++              reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
++
++      err = mv88e6xxx_led_get_selector(p, led, p->fiber, rules, &reg);
++      if (err)
++              return err;
++
++      reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL;
++
++      if (led == 0)
++              dev_dbg(p->chip->dev, "LED 0 hw control on port %d trigger selector 0x%02x\n",
++                      p->port,
++                      (unsigned int)(reg & MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK));
++      else
++              dev_dbg(p->chip->dev, "LED 1 hw control on port %d trigger selector 0x%02x\n",
++                      p->port,
++                      (unsigned int)(reg & MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK) >> 4);
++
++      return mv88e6xxx_port_led_write(p->chip, p->port, reg);
++}
++
++static int
++mv88e6xxx_led_hw_control_get(struct mv88e6xxx_port *p, int led, unsigned long *rules)
++{
++      u16 val;
++      int err;
++
++      mv88e6xxx_reg_lock(p->chip);
++      err = mv88e6xxx_port_led_read(p->chip, p->port,
++                                    MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL, &val);
++      mv88e6xxx_reg_unlock(p->chip);
++      if (err)
++              return err;
++
++      /* Mask out the selector bits for this port */
++      if (led == 1) {
++              val &= MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
++              /* It's forced blinking/OFF/ON */
++              if (val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELD ||
++                  val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELE ||
++                  val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELF) {
++                      *rules = 0;
++                      return 0;
++              }
++      } else {
++              val &= MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
++              /* It's forced blinking/OFF/ON */
++              if (val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELD ||
++                  val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELE ||
++                  val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELF) {
++                      *rules = 0;
++                      return 0;
++              }
++      }
++
++      err = mv88e6xxx_led_match_rule(p, val, led, rules);
++      if (!err)
++              return 0;
++
++      dev_dbg(p->chip->dev, "couldn't find matching selector for %04x\n", val);
++      *rules = 0;
++      return 0;
++}
++
++static int
++mv88e6xxx_led0_hw_control_set(struct led_classdev *ldev, unsigned long rules)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
++      int err;
++
++      mv88e6xxx_reg_lock(p->chip);
++      err = mv88e6xxx_led_hw_control_set(p, 0, rules);
++      mv88e6xxx_reg_unlock(p->chip);
++
++      return err;
++}
++
++static int
++mv88e6xxx_led1_hw_control_set(struct led_classdev *ldev, unsigned long rules)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
++      int err;
++
++      mv88e6xxx_reg_lock(p->chip);
++      err = mv88e6xxx_led_hw_control_set(p, 1, rules);
++      mv88e6xxx_reg_unlock(p->chip);
++
++      return err;
++}
++
++static int
++mv88e6xxx_led0_hw_control_get(struct led_classdev *ldev, unsigned long *rules)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
++
++      return mv88e6xxx_led_hw_control_get(p, 0, rules);
++}
++
++static int
++mv88e6xxx_led1_hw_control_get(struct led_classdev *ldev, unsigned long *rules)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
++
++      return mv88e6xxx_led_hw_control_get(p, 1, rules);
++}
++
++static struct device *mv88e6xxx_led_hw_control_get_device(struct mv88e6xxx_port *p)
++{
++      struct dsa_port *dp;
++
++      dp = dsa_to_port(p->chip->ds, p->port);
++      if (!dp)
++              return NULL;
++      if (dp->slave)
++              return &dp->slave->dev;
++      return NULL;
++}
++
++static struct device *
++mv88e6xxx_led0_hw_control_get_device(struct led_classdev *ldev)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
++
++      return mv88e6xxx_led_hw_control_get_device(p);
++}
++
++static struct device *
++mv88e6xxx_led1_hw_control_get_device(struct led_classdev *ldev)
++{
++      struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
++
++      return mv88e6xxx_led_hw_control_get_device(p);
++}
++
++int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, int port)
++{
++      struct fwnode_handle *led = NULL, *leds = NULL;
++      struct led_init_data init_data = { };
++      enum led_default_state state;
++      struct mv88e6xxx_port *p;
++      struct led_classdev *l;
++      struct device *dev;
++      u32 led_num;
++      int ret;
++
++      /* LEDs are on ports 1,2,3,4, 5 and 6 (index 0..5), no more */
++      if (port > 5)
++              return -EOPNOTSUPP;
++
++      p = &chip->ports[port];
++      if (!p->fwnode)
++              return 0;
++
++      dev = chip->dev;
++
++      leds = fwnode_get_named_child_node(p->fwnode, "leds");
++      if (!leds) {
++              dev_dbg(dev, "No Leds node specified in device tree for port %d!\n",
++                      port);
++              return 0;
++      }
++
++      fwnode_for_each_child_node(leds, led) {
++              /* Reg represent the led number of the port, max 2
++               * LEDs can be connected to each port, in some designs
++               * only one LED is connected.
++               */
++              if (fwnode_property_read_u32(led, "reg", &led_num))
++                      continue;
++              if (led_num > 1) {
++                      dev_err(dev, "invalid LED specified port %d\n", port);
++                      return -EINVAL;
++              }
++
++              if (led_num == 0)
++                      l = &p->led0;
++              else
++                      l = &p->led1;
++
++              state = led_init_default_state_get(led);
++              switch (state) {
++              case LEDS_DEFSTATE_ON:
++                      l->brightness = 1;
++                      mv88e6xxx_led_brightness_set(p, led_num, 1);
++                      break;
++              case LEDS_DEFSTATE_KEEP:
++                      break;
++              default:
++                      l->brightness = 0;
++                      mv88e6xxx_led_brightness_set(p, led_num, 0);
++              }
++
++              l->max_brightness = 1;
++              if (led_num == 0) {
++                      l->brightness_set_blocking = mv88e6xxx_led0_brightness_set_blocking;
++                      l->blink_set = mv88e6xxx_led0_blink_set;
++                      l->hw_control_is_supported = mv88e6xxx_led0_hw_control_is_supported;
++                      l->hw_control_set = mv88e6xxx_led0_hw_control_set;
++                      l->hw_control_get = mv88e6xxx_led0_hw_control_get;
++                      l->hw_control_get_device = mv88e6xxx_led0_hw_control_get_device;
++              } else {
++                      l->brightness_set_blocking = mv88e6xxx_led1_brightness_set_blocking;
++                      l->blink_set = mv88e6xxx_led1_blink_set;
++                      l->hw_control_is_supported = mv88e6xxx_led1_hw_control_is_supported;
++                      l->hw_control_set = mv88e6xxx_led1_hw_control_set;
++                      l->hw_control_get = mv88e6xxx_led1_hw_control_get;
++                      l->hw_control_get_device = mv88e6xxx_led1_hw_control_get_device;
++              }
++              l->hw_control_trigger = "netdev";
++
++              init_data.default_label = ":port";
++              init_data.fwnode = led;
++              init_data.devname_mandatory = true;
++              init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d:0%d", chip->info->name,
++                                               port, led_num);
++              if (!init_data.devicename)
++                      return -ENOMEM;
++
++              ret = devm_led_classdev_register_ext(dev, l, &init_data);
++              kfree(init_data.devicename);
++
++              if (ret) {
++                      dev_err(dev, "Failed to init LED %d for port %d", led_num, port);
++                      return ret;
++              }
++      }
++
++      return 0;
++}
+--- a/drivers/net/dsa/mv88e6xxx/port.c
++++ b/drivers/net/dsa/mv88e6xxx/port.c
+@@ -12,6 +12,7 @@
+ #include <linux/if_bridge.h>
+ #include <linux/phy.h>
+ #include <linux/phylink.h>
++#include <linux/property.h>
+ #include "chip.h"
+ #include "global2.h"
+--- a/drivers/net/dsa/mv88e6xxx/port.h
++++ b/drivers/net/dsa/mv88e6xxx/port.h
+@@ -309,6 +309,130 @@
+ /* Offset 0x13: OutFiltered Counter */
+ #define MV88E6XXX_PORT_OUT_FILTERED   0x13
++/* Offset 0x16: LED Control */
++#define MV88E6XXX_PORT_LED_CONTROL                            0x16
++#define MV88E6XXX_PORT_LED_CONTROL_UPDATE                     BIT(15)
++#define MV88E6XXX_PORT_LED_CONTROL_POINTER_MASK                       GENMASK(14, 12)
++#define MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL         (0x00 << 12) /* Control for LED 0 and 1 */
++#define MV88E6XXX_PORT_LED_CONTROL_POINTER_STRETCH_BLINK      (0x06 << 12) /* Stetch and Blink Rate */
++#define MV88E6XXX_PORT_LED_CONTROL_POINTER_CNTL_SPECIAL               (0x07 << 12) /* Control for the Port's Special LED */
++#define MV88E6XXX_PORT_LED_CONTROL_DATA_MASK                  GENMASK(10, 0)
++/* Selection masks valid for either port 1,2,3,4 or 5 */
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK              GENMASK(3, 0)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK              GENMASK(7, 4)
++/* Selection control for LED 0 and 1, ports 5 and 6 only has LED 0
++ * Bits  Function
++ * 0..3  LED 0 control selector on ports 1-5
++ * 4..7  LED 1 control selector on ports 1-4 on port 5 this controls LED 0 of port 6
++ *
++ * Sel Port LED Function for the 6352 family:
++ * 0   1-4  0   Link/Act/Speed by Blink Rate (off=no link, on=link, blink=activity, blink speed=link speed)
++ *     1-4  1   Port 2's Special LED
++ *     5-6  0   Port 5 Link/Act (off=no link, on=link, blink=activity)
++ *     5-6  1   Port 6 Link/Act (off=no link, on=link 1000, blink=activity)
++ * 1   1-4  0   100/1000 Link/Act (off=no link, on=100 or 1000 link, blink=activity)
++ *     1-4  1   10/100 Link Act (off=no link, on=10 or 100 link, blink=activity)
++ *     5-6  0   Fiber 100 Link/Act (off=no link, on=link 100, blink=activity)
++ *     5-6  1   Fiber 1000 Link/Act (off=no link, on=link 1000, blink=activity)
++ * 2   1-4  0   1000 Link/Act (off=no link, on=link 1000, blink=activity)
++ *     1-4  1   10/100 Link/Act (off=no link, on=10 or 100 link, blink=activity)
++ *     5-6  0   Fiber 1000 Link/Act (off=no link, on=link 1000, blink=activity)
++ *     5-6  1   Fiber 100 Link/Act (off=no link, on=link 100, blink=activity)
++ * 3   1-4  0   Link/Act (off=no link, on=link, blink=activity)
++ *     1-4  1   1000 Link (off=no link, on=1000 link)
++ *     5-6  0   Port 0's Special LED
++ *     5-6  1   Fiber Link (off=no link, on=link)
++ * 4   1-4  0   Port 0's Special LED
++ *     1-4  1   Port 1's Special LED
++ *     5-6  0   Port 1's Special LED
++ *     5-6  1   Port 5 Link/Act (off=no link, on=link, blink=activity)
++ * 5   1-4  0   Reserved
++ *     1-4  1   Reserved
++ *     5-6  0   Port 2's Special LED
++ *     5-6  1   Port 6 Link (off=no link, on=link)
++ * 6   1-4  0   Duplex/Collision (off=half-duplex,on=full-duplex,blink=collision)
++ *     1-4  1   10/1000 Link/Act (off=no link, on=10 or 1000 link, blink=activity)
++ *     5-6  0   Port 5 Duplex/Collision (off=half-duplex, on=full-duplex, blink=col)
++ *     5-6  1   Port 6 Duplex/Collision (off=half-duplex, on=full-duplex, blink=col)
++ * 7   1-4  0   10/1000 Link/Act (off=no link, on=10 or 1000 link, blink=activity)
++ *     1-4  1   10/1000 Link (off=no link, on=10 or 1000 link)
++ *     5-6  0   Port 5 Link/Act/Speed by Blink rate (off=no link, on=link, blink=activity, blink speed=link speed)
++ *     5-6  1   Port 6 Link/Act/Speed by Blink rate (off=no link, on=link, blink=activity,  blink speed=link speed)
++ * 8   1-4  0   Link (off=no link, on=link)
++ *     1-4  1   Activity (off=no link, blink on=activity)
++ *     5-6  0   Port 6 Link/Act (off=no link, on=link, blink=activity)
++ *     5-6  1   Port 0's Special LED
++ * 9   1-4  0   10 Link (off=no link, on=10 link)
++ *     1-4  1   100 Link (off=no link, on=100 link)
++ *     5-6  0   Reserved
++ *     5-6  1   Port 1's Special LED
++ * a   1-4  0   10 Link/Act (off=no link, on=10 link, blink=activity)
++ *     1-4  1   100 Link/Act (off=no link, on=100 link, blink=activity)
++ *     5-6  0   Reserved
++ *     5-6  1   Port 2's Special LED
++ * b   1-4  0   100/1000 Link (off=no link, on=100 or 1000 link)
++ *     1-4  1   10/100 Link (off=no link, on=100 link, blink=activity)
++ *     5-6  0   Reserved
++ *     5-6  1   Reserved
++ * c     *  *   PTP Act (blink on=PTP activity)
++ * d     *  *   Force Blink
++ * e     *  *   Force Off
++ * f     *  *   Force On
++ */
++/* Select LED0 output */
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL0                  0x0
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1                  0x1
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2                  0x2
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL3                  0x3
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL4                  0x4
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL5                  0x5
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6                  0x6
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL7                  0x7
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8                  0x8
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL9                  0x9
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELA                  0xa
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELB                  0xb
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELC                  0xc
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELD                  0xd
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELE                  0xe
++#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELF                  0xf
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL0                  (0x0 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1                  (0x1 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2                  (0x2 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3                  (0x3 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL4                  (0x4 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL5                  (0x5 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6                  (0x6 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL7                  (0x7 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL8                  (0x8 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL9                  (0x9 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELA                  (0xa << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELB                  (0xb << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELC                  (0xc << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELD                  (0xd << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELE                  (0xe << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELF                  (0xf << 4)
++/* Stretch and Blink Rate Control (Index 0x06 of LED Control) */
++/* Pulse Stretch Selection for all LED's on this port */
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_NONE    (0 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS    (1 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_42MS    (2 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_84MS    (3 << 4)
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_168MS   (4 << 4)
++/* Blink Rate Selection for all LEDs on this port */
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_21MS               0
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_42MS               1
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_84MS               2
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_168MS      3
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_336MS      4
++#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_672MS      5
++ /* Control for Special LED (Index 0x7 of LED Control on Port0) */
++#define MV88E6XXX_PORT_LED_CONTROL_0x07_P0_LAN_LINKACT_SHIFT  0 /* bits 6:0 LAN Link Activity LED */
++/* Control for Special LED (Index 0x7 of LED Control on Port 1) */
++#define MV88E6XXX_PORT_LED_CONTROL_0x07_P1_WAN_LINKACT_SHIFT  0 /* bits 6:0 WAN Link Activity LED */
++/* Control for Special LED (Index 0x7 of LED Control on Port 2) */
++#define MV88E6XXX_PORT_LED_CONTROL_0x07_P2_PTP_ACT            0 /* bits 6:0 PTP Activity */
++
+ /* Offset 0x18: IEEE Priority Mapping Table */
+ #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE                    0x18
+ #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE             0x8000
+@@ -457,6 +581,15 @@ int mv88e6393x_port_set_cmode(struct mv8
+                             phy_interface_t mode);
+ int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
+ int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
++#ifdef CONFIG_NET_DSA_MV88E6XXX_LEDS
++int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, int port);
++#else
++static inline int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip,
++                                          int port)
++{
++      return 0;
++}
++#endif
+ int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
+                                bool drop_untagged);
+ int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port, bool map);
diff --git a/target/linux/generic/config-6.6 b/target/linux/generic/config-6.6
new file mode 100644 (file)
index 0000000..105dc0d
--- /dev/null
@@ -0,0 +1,7552 @@
+# CONFIG_104_QUAD_8 is not set
+CONFIG_32BIT=y
+# CONFIG_6LOWPAN is not set
+# CONFIG_6LOWPAN_DEBUGFS is not set
+# CONFIG_6PACK is not set
+# CONFIG_8139CP is not set
+# CONFIG_8139TOO is not set
+# CONFIG_9P_FS is not set
+# CONFIG_AB8500_CORE is not set
+# CONFIG_ABP060MG is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_ACENIC is not set
+# CONFIG_ACERHDF is not set
+# CONFIG_ACER_WIRELESS is not set
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_ACPI_ALS is not set
+# CONFIG_ACPI_APEI is not set
+# CONFIG_ACPI_APEI_PCIEAER is not set
+# CONFIG_ACPI_BUTTON is not set
+# CONFIG_ACPI_CONFIGFS is not set
+# CONFIG_ACPI_CUSTOM_METHOD is not set
+# CONFIG_ACPI_EXTLOG is not set
+# CONFIG_ACPI_HED is not set
+# CONFIG_ACPI_NFIT is not set
+# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set
+# CONFIG_ACPI_TABLE_UPGRADE is not set
+# CONFIG_ACPI_VIDEO is not set
+# CONFIG_AD2S1200 is not set
+# CONFIG_AD2S1210 is not set
+# CONFIG_AD2S90 is not set
+# CONFIG_AD3552R is not set
+# CONFIG_AD4130 is not set
+# CONFIG_AD5064 is not set
+# CONFIG_AD5110 is not set
+# CONFIG_AD525X_DPOT is not set
+# CONFIG_AD5272 is not set
+# CONFIG_AD5360 is not set
+# CONFIG_AD5380 is not set
+# CONFIG_AD5421 is not set
+# CONFIG_AD5446 is not set
+# CONFIG_AD5449 is not set
+# CONFIG_AD5504 is not set
+# CONFIG_AD5592R is not set
+# CONFIG_AD5593R is not set
+# CONFIG_AD5624R_SPI is not set
+# CONFIG_AD5686 is not set
+# CONFIG_AD5686_SPI is not set
+# CONFIG_AD5696_I2C is not set
+# CONFIG_AD5755 is not set
+# CONFIG_AD5758 is not set
+# CONFIG_AD5761 is not set
+# CONFIG_AD5764 is not set
+# CONFIG_AD5766 is not set
+# CONFIG_AD5770R is not set
+# CONFIG_AD5791 is not set
+# CONFIG_AD5933 is not set
+# CONFIG_AD7091R5 is not set
+# CONFIG_AD7124 is not set
+# CONFIG_AD7150 is not set
+# CONFIG_AD7192 is not set
+# CONFIG_AD7266 is not set
+# CONFIG_AD7280 is not set
+# CONFIG_AD7291 is not set
+# CONFIG_AD7292 is not set
+# CONFIG_AD7293 is not set
+# CONFIG_AD7298 is not set
+# CONFIG_AD7303 is not set
+# CONFIG_AD74115 is not set
+# CONFIG_AD74413R is not set
+# CONFIG_AD7476 is not set
+# CONFIG_AD7606 is not set
+# CONFIG_AD7606_IFACE_PARALLEL is not set
+# CONFIG_AD7606_IFACE_SPI is not set
+# CONFIG_AD7746 is not set
+# CONFIG_AD7766 is not set
+# CONFIG_AD7768_1 is not set
+# CONFIG_AD7780 is not set
+# CONFIG_AD7791 is not set
+# CONFIG_AD7793 is not set
+# CONFIG_AD7816 is not set
+# CONFIG_AD7887 is not set
+# CONFIG_AD7923 is not set
+# CONFIG_AD7949 is not set
+# CONFIG_AD799X is not set
+# CONFIG_AD8366 is not set
+# CONFIG_AD8801 is not set
+# CONFIG_AD9467 is not set
+# CONFIG_AD9523 is not set
+# CONFIG_AD9832 is not set
+# CONFIG_AD9834 is not set
+# CONFIG_ADA4250 is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_ADF4350 is not set
+# CONFIG_ADF4371 is not set
+# CONFIG_ADF4377 is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADIN1100_PHY is not set
+# CONFIG_ADIN1110 is not set
+# CONFIG_ADIN_PHY is not set
+# CONFIG_ADIS16080 is not set
+# CONFIG_ADIS16130 is not set
+# CONFIG_ADIS16136 is not set
+# CONFIG_ADIS16201 is not set
+# CONFIG_ADIS16203 is not set
+# CONFIG_ADIS16209 is not set
+# CONFIG_ADIS16240 is not set
+# CONFIG_ADIS16260 is not set
+# CONFIG_ADIS16400 is not set
+# CONFIG_ADIS16460 is not set
+# CONFIG_ADIS16475 is not set
+# CONFIG_ADIS16480 is not set
+# CONFIG_ADI_AXI_ADC is not set
+# CONFIG_ADJD_S311 is not set
+# CONFIG_ADM6996_PHY is not set
+# CONFIG_ADM8211 is not set
+# CONFIG_ADMV1013 is not set
+# CONFIG_ADMV1014 is not set
+# CONFIG_ADMV4420 is not set
+# CONFIG_ADMV8818 is not set
+# CONFIG_ADRF6780 is not set
+# CONFIG_ADT7316 is not set
+# CONFIG_ADUX1020 is not set
+CONFIG_ADVISE_SYSCALLS=y
+# CONFIG_ADXL313_I2C is not set
+# CONFIG_ADXL313_SPI is not set
+# CONFIG_ADXL345_I2C is not set
+# CONFIG_ADXL345_SPI is not set
+# CONFIG_ADXL355_I2C is not set
+# CONFIG_ADXL355_SPI is not set
+# CONFIG_ADXL367_I2C is not set
+# CONFIG_ADXL367_SPI is not set
+# CONFIG_ADXL372_I2C is not set
+# CONFIG_ADXL372_SPI is not set
+# CONFIG_ADXRS290 is not set
+# CONFIG_ADXRS450 is not set
+CONFIG_AEABI=y
+# CONFIG_AFE4403 is not set
+# CONFIG_AFE4404 is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_AFS_DEBUG_CURSOR is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_AF_KCM is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_AF_RXRPC_INJECT_LOSS is not set
+# CONFIG_AF_RXRPC_INJECT_RX_DELAY is not set
+# CONFIG_AF_RXRPC_IPV6 is not set
+CONFIG_AF_UNIX_OOB=y
+# CONFIG_AGP is not set
+# CONFIG_AHCI_BRCM is not set
+# CONFIG_AHCI_CEVA is not set
+# CONFIG_AHCI_DWC is not set
+# CONFIG_AHCI_IMX is not set
+# CONFIG_AHCI_MVEBU is not set
+# CONFIG_AHCI_QORIQ is not set
+# CONFIG_AHCI_XGENE is not set
+CONFIG_AIO=y
+# CONFIG_AIRO is not set
+# CONFIG_AIRO_CS is not set
+# CONFIG_AIR_EN8811H_PHY is not set
+# CONFIG_AIX_PARTITION is not set
+# CONFIG_AK09911 is not set
+# CONFIG_AK8974 is not set
+# CONFIG_AK8975 is not set
+# CONFIG_AL3010 is not set
+# CONFIG_AL3320A is not set
+# CONFIG_ALIM7101_WDT is not set
+CONFIG_ALLOW_DEV_COREDUMP=y
+# CONFIG_ALTERA_MBOX is not set
+# CONFIG_ALTERA_MSGDMA is not set
+# CONFIG_ALTERA_STAPL is not set
+# CONFIG_ALTERA_TSE is not set
+# CONFIG_ALX is not set
+# CONFIG_AL_FIC is not set
+# CONFIG_AM2315 is not set
+# CONFIG_AM335X_PHY_USB is not set
+# CONFIG_AMBA_PL08X is not set
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_AMD_MEM_ENCRYPT is not set
+# CONFIG_AMD_PHY is not set
+# CONFIG_AMD_XGBE is not set
+# CONFIG_AMD_XGBE_DCB is not set
+# CONFIG_AMD_XGBE_HAVE_ECC is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_AMILO_RFKILL is not set
+# CONFIG_AMPERE_ERRATUM_AC03_CPU_38 is not set
+# CONFIG_AMT is not set
+# CONFIG_ANDROID_BINDER_IPC is not set
+# CONFIG_ANON_VMA_NAME is not set
+# CONFIG_APDS9300 is not set
+# CONFIG_APDS9802ALS is not set
+# CONFIG_APDS9960 is not set
+# CONFIG_APM8018X is not set
+# CONFIG_APM_EMULATION is not set
+# CONFIG_APPLE_GMUX is not set
+# CONFIG_APPLE_MFI_FASTCHARGE is not set
+# CONFIG_APPLE_PROPERTIES is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_AQTION is not set
+# CONFIG_AQUANTIA_PHY is not set
+# CONFIG_AR5523 is not set
+# CONFIG_AR7 is not set
+# CONFIG_AR8216_PHY is not set
+# CONFIG_AR8216_PHY_LEDS is not set
+# CONFIG_ARCH_ACTIONS is not set
+# CONFIG_ARCH_AIROHA is not set
+# CONFIG_ARCH_ALPINE is not set
+# CONFIG_ARCH_APPLE is not set
+# CONFIG_ARCH_ARTPEC is not set
+# CONFIG_ARCH_ASPEED is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_AXXIA is not set
+# CONFIG_ARCH_BCM is not set
+# CONFIG_ARCH_BCM2835 is not set
+# CONFIG_ARCH_BCMBCA is not set
+# CONFIG_ARCH_BCM_21664 is not set
+# CONFIG_ARCH_BCM_23550 is not set
+# CONFIG_ARCH_BCM_281XX is not set
+# CONFIG_ARCH_BCM_5301X is not set
+# CONFIG_ARCH_BCM_53573 is not set
+# CONFIG_ARCH_BCM_CYGNUS is not set
+# CONFIG_ARCH_BCM_HR2 is not set
+# CONFIG_ARCH_BCM_IPROC is not set
+# CONFIG_ARCH_BCM_NSP is not set
+# CONFIG_ARCH_BERLIN is not set
+CONFIG_ARCH_BINFMT_ELF_STATE=y
+# CONFIG_ARCH_BITMAIN is not set
+# CONFIG_ARCH_BRCMSTB is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_DIGICOLOR is not set
+# CONFIG_ARCH_DMA_ADDR_T_64BIT is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_EXYNOS is not set
+CONFIG_ARCH_FLATMEM_ENABLE=y
+# CONFIG_ARCH_FOOTBRIDGE is not set
+CONFIG_ARCH_FORCE_MAX_ORDER=11
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_HI3xxx is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_HISI is not set
+# CONFIG_ARCH_HPE is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_INTEL_SOCFPGA is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_K3 is not set
+# CONFIG_ARCH_KEEMBAY is not set
+# CONFIG_ARCH_KEYSTONE is not set
+# CONFIG_ARCH_LAYERSCAPE is not set
+# CONFIG_ARCH_LG1K is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_MA35 is not set
+# CONFIG_ARCH_MEDIATEK is not set
+# CONFIG_ARCH_MESON is not set
+# CONFIG_ARCH_MILBEAUT is not set
+CONFIG_ARCH_MMAP_RND_BITS=8
+CONFIG_ARCH_MMAP_RND_BITS_MAX=16
+CONFIG_ARCH_MMAP_RND_BITS_MIN=8
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_MSTARV7 is not set
+# CONFIG_ARCH_MULTIPLATFORM is not set
+# CONFIG_ARCH_MULTI_V6 is not set
+# CONFIG_ARCH_MULTI_V7 is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MVEBU is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_MXS is not set
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_NPCM is not set
+# CONFIG_ARCH_NSPIRE is not set
+# CONFIG_ARCH_NXP is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_OMAP1 is not set
+# CONFIG_ARCH_OMAP2 is not set
+# CONFIG_ARCH_OMAP2PLUS is not set
+# CONFIG_ARCH_OMAP3 is not set
+# CONFIG_ARCH_OMAP4 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_QCOM is not set
+# CONFIG_ARCH_RDA is not set
+# CONFIG_ARCH_REALTEK is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_RENESAS is not set
+# CONFIG_ARCH_ROCKCHIP is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_S32 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_SEATTLE is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_SPARX5 is not set
+# CONFIG_ARCH_SPRD is not set
+# CONFIG_ARCH_STI is not set
+# CONFIG_ARCH_STM32 is not set
+# CONFIG_ARCH_SUNPLUS is not set
+# CONFIG_ARCH_SUNXI is not set
+# CONFIG_ARCH_SYNQUACER is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_THUNDER is not set
+# CONFIG_ARCH_THUNDER2 is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_UNIPHIER is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_VIRT is not set
+# CONFIG_ARCH_VISCONTI is not set
+# CONFIG_ARCH_VT8500 is not set
+# CONFIG_ARCH_WANTS_THP_SWAP is not set
+# CONFIG_ARCH_WM8505 is not set
+# CONFIG_ARCH_WM8750 is not set
+# CONFIG_ARCH_WM8850 is not set
+# CONFIG_ARCH_XGENE is not set
+# CONFIG_ARCH_ZYNQ is not set
+# CONFIG_ARCH_ZYNQMP is not set
+# CONFIG_ARCNET is not set
+# CONFIG_ARC_EMAC is not set
+# CONFIG_ARC_IRQ_NO_AUTOSAVE is not set
+# CONFIG_ARM64_16K_PAGES is not set
+# CONFIG_ARM64_64K_PAGES is not set
+# CONFIG_ARM64_AMU_EXTN is not set
+# CONFIG_ARM64_BTI is not set
+CONFIG_ARM64_CNP=y
+# CONFIG_ARM64_E0PD is not set
+# CONFIG_ARM64_EPAN is not set
+# CONFIG_ARM64_ERRATUM_1024718 is not set
+# CONFIG_ARM64_ERRATUM_1165522 is not set
+# CONFIG_ARM64_ERRATUM_1286807 is not set
+# CONFIG_ARM64_ERRATUM_1319367 is not set
+# CONFIG_ARM64_ERRATUM_1418040 is not set
+# CONFIG_ARM64_ERRATUM_1463225 is not set
+# CONFIG_ARM64_ERRATUM_1508412 is not set
+# CONFIG_ARM64_ERRATUM_1530923 is not set
+# CONFIG_ARM64_ERRATUM_1542419 is not set
+# CONFIG_ARM64_ERRATUM_1742098 is not set
+# CONFIG_ARM64_ERRATUM_2051678 is not set
+# CONFIG_ARM64_ERRATUM_2054223 is not set
+# CONFIG_ARM64_ERRATUM_2067961 is not set
+# CONFIG_ARM64_ERRATUM_2077057 is not set
+# CONFIG_ARM64_ERRATUM_2441007 is not set
+# CONFIG_ARM64_ERRATUM_2441009 is not set
+# CONFIG_ARM64_ERRATUM_2645198 is not set
+# CONFIG_ARM64_ERRATUM_2658417 is not set
+# CONFIG_ARM64_ERRATUM_2966298 is not set
+# CONFIG_ARM64_ERRATUM_3117295 is not set
+# CONFIG_ARM64_ERRATUM_3194386 is not set
+# CONFIG_ARM64_ERRATUM_819472 is not set
+# CONFIG_ARM64_ERRATUM_824069 is not set
+# CONFIG_ARM64_ERRATUM_826319 is not set
+# CONFIG_ARM64_ERRATUM_827319 is not set
+# CONFIG_ARM64_ERRATUM_832075 is not set
+# CONFIG_ARM64_ERRATUM_834220 is not set
+# CONFIG_ARM64_ERRATUM_843419 is not set
+# CONFIG_ARM64_ERRATUM_845719 is not set
+# CONFIG_ARM64_ERRATUM_858921 is not set
+# CONFIG_ARM64_HW_AFDBM is not set
+# CONFIG_ARM64_LSE_ATOMICS is not set
+# CONFIG_ARM64_MTE is not set
+CONFIG_ARM64_PAN=y
+# CONFIG_ARM64_PMEM is not set
+# CONFIG_ARM64_PSEUDO_NMI is not set
+# CONFIG_ARM64_PTR_AUTH is not set
+# CONFIG_ARM64_RAS_EXTN is not set
+# CONFIG_ARM64_RELOC_TEST is not set
+# CONFIG_ARM64_SME is not set
+# CONFIG_ARM64_SVE is not set
+CONFIG_ARM64_SW_TTBR0_PAN=y
+# CONFIG_ARM64_TLB_RANGE is not set
+# CONFIG_ARM64_USE_LSE_ATOMICS is not set
+# CONFIG_ARM64_VA_BITS_48 is not set
+# CONFIG_ARM_APPENDED_DTB is not set
+# CONFIG_ARM_ARCH_TIMER is not set
+# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set
+# CONFIG_ARM_CCI is not set
+# CONFIG_ARM_CCI400_PMU is not set
+# CONFIG_ARM_CCI5xx_PMU is not set
+# CONFIG_ARM_CCI_PMU is not set
+# CONFIG_ARM_CCN is not set
+# CONFIG_ARM_CMN is not set
+# CONFIG_ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU is not set
+# CONFIG_ARM_CPUIDLE is not set
+CONFIG_ARM_CPU_TOPOLOGY=y
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+# CONFIG_ARM_DSU_PMU is not set
+# CONFIG_ARM_ERRATA_326103 is not set
+# CONFIG_ARM_ERRATA_364296 is not set
+# CONFIG_ARM_ERRATA_411920 is not set
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+# CONFIG_ARM_ERRATA_643719 is not set
+# CONFIG_ARM_ERRATA_720789 is not set
+# CONFIG_ARM_ERRATA_742230 is not set
+# CONFIG_ARM_ERRATA_742231 is not set
+# CONFIG_ARM_ERRATA_743622 is not set
+# CONFIG_ARM_ERRATA_751472 is not set
+# CONFIG_ARM_ERRATA_754322 is not set
+# CONFIG_ARM_ERRATA_754327 is not set
+# CONFIG_ARM_ERRATA_764319 is not set
+# CONFIG_ARM_ERRATA_764369 is not set
+# CONFIG_ARM_ERRATA_773022 is not set
+# CONFIG_ARM_ERRATA_775420 is not set
+# CONFIG_ARM_ERRATA_798181 is not set
+# CONFIG_ARM_ERRATA_814220 is not set
+# CONFIG_ARM_ERRATA_818325_852422 is not set
+# CONFIG_ARM_ERRATA_821420 is not set
+# CONFIG_ARM_ERRATA_825619 is not set
+# CONFIG_ARM_ERRATA_852421 is not set
+# CONFIG_ARM_ERRATA_852423 is not set
+# CONFIG_ARM_ERRATA_857271 is not set
+# CONFIG_ARM_ERRATA_857272 is not set
+# CONFIG_ARM_FFA_TRANSPORT is not set
+CONFIG_ARM_GIC_MAX_NR=1
+# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
+# CONFIG_ARM_KPROBES_TEST is not set
+# CONFIG_ARM_LPAE is not set
+# CONFIG_ARM_MEDIATEK_CPUFREQ_HW is not set
+# CONFIG_ARM_MHU is not set
+CONFIG_ARM_MODULE_PLTS=y
+# CONFIG_ARM_PATCH_PHYS_VIRT is not set
+# CONFIG_ARM_PSCI is not set
+# CONFIG_ARM_PSCI_CHECKER is not set
+# CONFIG_ARM_PSCI_CPUIDLE is not set
+# CONFIG_ARM_PTDUMP_DEBUGFS is not set
+# CONFIG_ARM_SBSA_WATCHDOG is not set
+# CONFIG_ARM_SCMI_PROTOCOL is not set
+# CONFIG_ARM_SCPI_PROTOCOL is not set
+# CONFIG_ARM_SDE_INTERFACE is not set
+# CONFIG_ARM_SMCCC_SOC_ID is not set
+# CONFIG_ARM_SMC_WATCHDOG is not set
+# CONFIG_ARM_SMMU_V3_PMU is not set
+# CONFIG_ARM_SP805_WATCHDOG is not set
+# CONFIG_ARM_SPE_PMU is not set
+# CONFIG_ARM_THUMBEE is not set
+# CONFIG_ARM_TIMER_SP804 is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_ARM_VIRT_EXT is not set
+# CONFIG_AS3935 is not set
+# CONFIG_AS73211 is not set
+# CONFIG_ASM9260_TIMER is not set
+# CONFIG_ASN1 is not set
+# CONFIG_ASUS_LAPTOP is not set
+# CONFIG_ASUS_WIRELESS is not set
+# CONFIG_ASYMMETRIC_KEY_TYPE is not set
+# CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE is not set
+# CONFIG_ASYNC_RAID6_TEST is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_AT76C50X_USB is not set
+# CONFIG_AT803X_PHY is not set
+# CONFIG_AT91_SAMA5D2_ADC is not set
+# CONFIG_ATA is not set
+# CONFIG_ATAGS is not set
+CONFIG_ATAGS_PROC=y
+# CONFIG_ATALK is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_ATA_ACPI is not set
+CONFIG_ATA_BMDMA=y
+# CONFIG_ATA_FORCE is not set
+# CONFIG_ATA_GENERIC is not set
+# CONFIG_ATA_LEDS is not set
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_ATA_PIIX is not set
+CONFIG_ATA_SFF=y
+# CONFIG_ATA_VERBOSE_ERROR is not set
+# CONFIG_ATH10K is not set
+# CONFIG_ATH25 is not set
+# CONFIG_ATH5K is not set
+# CONFIG_ATH6KL is not set
+# CONFIG_ATH79 is not set
+# CONFIG_ATH9K is not set
+# CONFIG_ATH9K_HTC is not set
+# CONFIG_ATH_DEBUG is not set
+# CONFIG_ATL1 is not set
+# CONFIG_ATL1C is not set
+# CONFIG_ATL1E is not set
+# CONFIG_ATL2 is not set
+# CONFIG_ATLAS_EZO_SENSOR is not set
+# CONFIG_ATLAS_PH_SENSOR is not set
+# CONFIG_ATM is not set
+# CONFIG_ATMEL is not set
+# CONFIG_ATMEL_PIT is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ATM_BR2684 is not set
+CONFIG_ATM_BR2684_IPFILTER=y
+# CONFIG_ATM_CLIP is not set
+CONFIG_ATM_CLIP_NO_ICMP=y
+# CONFIG_ATM_DRIVERS is not set
+# CONFIG_ATM_DUMMY is not set
+# CONFIG_ATM_ENI is not set
+# CONFIG_ATM_FORE200E is not set
+# CONFIG_ATM_HE is not set
+# CONFIG_ATM_IA is not set
+# CONFIG_ATM_IDT77252 is not set
+# CONFIG_ATM_LANAI is not set
+# CONFIG_ATM_LANE is not set
+# CONFIG_ATM_MPOA is not set
+# CONFIG_ATM_NICSTAR is not set
+# CONFIG_ATM_SOLOS is not set
+# CONFIG_ATM_TCP is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_ATP is not set
+# CONFIG_AUDIT is not set
+# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTO_ZRELADDR is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_AX25 is not set
+# CONFIG_AX25_DAMA_SLAVE is not set
+# CONFIG_AX88796 is not set
+# CONFIG_AX88796B_PHY is not set
+# CONFIG_AXP20X_ADC is not set
+# CONFIG_AXP20X_POWER is not set
+# CONFIG_AXP288_ADC is not set
+# CONFIG_AXP288_FUEL_GAUGE is not set
+# CONFIG_B43 is not set
+# CONFIG_B43LEGACY is not set
+# CONFIG_B44 is not set
+# CONFIG_B53 is not set
+# CONFIG_B53_MDIO_DRIVER is not set
+# CONFIG_B53_MMAP_DRIVER is not set
+# CONFIG_B53_SERDES is not set
+# CONFIG_B53_SPI_DRIVER is not set
+# CONFIG_B53_SRAB_DRIVER is not set
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+# CONFIG_BACKLIGHT_APPLE is not set
+# CONFIG_BACKLIGHT_ARCXCNN is not set
+# CONFIG_BACKLIGHT_BD6107 is not set
+# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
+# CONFIG_BACKLIGHT_GPIO is not set
+# CONFIG_BACKLIGHT_KTD253 is not set
+# CONFIG_BACKLIGHT_KTZ8866 is not set
+# CONFIG_BACKLIGHT_LED is not set
+# CONFIG_BACKLIGHT_LM3630A is not set
+# CONFIG_BACKLIGHT_LM3639 is not set
+# CONFIG_BACKLIGHT_LP855X is not set
+# CONFIG_BACKLIGHT_LV5207LP is not set
+# CONFIG_BACKLIGHT_PANDORA is not set
+# CONFIG_BACKLIGHT_PWM is not set
+# CONFIG_BACKLIGHT_QCOM_WLED is not set
+# CONFIG_BACKLIGHT_SAHARA is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_BACKTRACE_VERBOSE is not set
+# CONFIG_BAREUDP is not set
+CONFIG_BASE_FULL=y
+CONFIG_BASE_SMALL=0
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_BATTERY_BQ27XXX is not set
+# CONFIG_BATTERY_BQ27XXX_HDQ is not set
+# CONFIG_BATTERY_CW2015 is not set
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_BATTERY_DS2780 is not set
+# CONFIG_BATTERY_DS2781 is not set
+# CONFIG_BATTERY_DS2782 is not set
+# CONFIG_BATTERY_GAUGE_LTC2941 is not set
+# CONFIG_BATTERY_GOLDFISH is not set
+# CONFIG_BATTERY_LEGO_EV3 is not set
+# CONFIG_BATTERY_MAX17040 is not set
+# CONFIG_BATTERY_MAX17042 is not set
+# CONFIG_BATTERY_MAX1721X is not set
+# CONFIG_BATTERY_RT5033 is not set
+# CONFIG_BATTERY_SAMSUNG_SDI is not set
+# CONFIG_BATTERY_SBS is not set
+# CONFIG_BATTERY_UG3105 is not set
+# CONFIG_BAYCOM_EPP is not set
+# CONFIG_BAYCOM_PAR is not set
+# CONFIG_BAYCOM_SER_FDX is not set
+# CONFIG_BAYCOM_SER_HDX is not set
+# CONFIG_BCACHE is not set
+# CONFIG_BCM47XX is not set
+# CONFIG_BCM54140_PHY is not set
+# CONFIG_BCM63XX is not set
+# CONFIG_BCM63XX_PHY is not set
+# CONFIG_BCM7038_L1_IRQ is not set
+# CONFIG_BCM7038_WDT is not set
+# CONFIG_BCM7120_L2_IRQ is not set
+# CONFIG_BCM7XXX_PHY is not set
+# CONFIG_BCM84881_PHY is not set
+# CONFIG_BCM87XX_PHY is not set
+# CONFIG_BCMA is not set
+# CONFIG_BCMA_DRIVER_GPIO is not set
+CONFIG_BCMA_POSSIBLE=y
+# CONFIG_BCMGENET is not set
+# CONFIG_BCM_IPROC_ADC is not set
+# CONFIG_BCM_KONA_USB2_PHY is not set
+# CONFIG_BCM_SBA_RAID is not set
+# CONFIG_BCM_VK is not set
+# CONFIG_BDI_SWITCH is not set
+# CONFIG_BE2ISCSI is not set
+# CONFIG_BE2NET is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_BGMAC is not set
+# CONFIG_BH1750 is not set
+# CONFIG_BH1780 is not set
+# CONFIG_BIG_KEYS is not set
+# CONFIG_BIG_LITTLE is not set
+CONFIG_BINARY_PRINTF=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_ELF_FDPIC is not set
+# CONFIG_BINFMT_FLAT is not set
+# CONFIG_BINFMT_MISC is not set
+CONFIG_BINFMT_SCRIPT=y
+CONFIG_BITREVERSE=y
+# CONFIG_BLK_CGROUP_IOCOST is not set
+# CONFIG_BLK_CGROUP_IOLATENCY is not set
+# CONFIG_BLK_CGROUP_IOPRIO is not set
+# CONFIG_BLK_DEBUG_FS is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_DM is not set
+# CONFIG_BLK_DEV_DRBD is not set
+# CONFIG_BLK_DEV_FD is not set
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_INTEGRITY is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_NULL_BLK is not set
+# CONFIG_BLK_DEV_NVME is not set
+# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set
+# CONFIG_BLK_DEV_PMEM is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_RBD is not set
+# CONFIG_BLK_DEV_SD is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_BLK_DEV_THROTTLING is not set
+# CONFIG_BLK_DEV_UBLK is not set
+# CONFIG_BLK_DEV_ZONED is not set
+# CONFIG_BLK_INLINE_ENCRYPTION is not set
+# CONFIG_BLK_SED_OPAL is not set
+# CONFIG_BLK_WBT is not set
+CONFIG_BLOCK=y
+# CONFIG_BLOCK_LEGACY_AUTOLOAD is not set
+# CONFIG_BLOCK_NOTIFIERS is not set
+# CONFIG_BMA180 is not set
+# CONFIG_BMA220 is not set
+# CONFIG_BMA400 is not set
+# CONFIG_BMC150_ACCEL is not set
+# CONFIG_BMC150_MAGN is not set
+# CONFIG_BMC150_MAGN_I2C is not set
+# CONFIG_BMC150_MAGN_SPI is not set
+# CONFIG_BME680 is not set
+# CONFIG_BMG160 is not set
+# CONFIG_BMI088_ACCEL is not set
+# CONFIG_BMI160_I2C is not set
+# CONFIG_BMI160_SPI is not set
+# CONFIG_BMIPS_GENERIC is not set
+# CONFIG_BMP280 is not set
+# CONFIG_BNA is not set
+# CONFIG_BNX2 is not set
+# CONFIG_BNX2X is not set
+# CONFIG_BNX2X_SRIOV is not set
+# CONFIG_BNXT is not set
+# CONFIG_BONDING is not set
+# CONFIG_BOOKE_WDT is not set
+CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT=3
+# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+# CONFIG_BOOTTIME_TRACING is not set
+# CONFIG_BOOT_CONFIG is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+CONFIG_BOOT_RAW=y
+# CONFIG_BOSCH_BNO055_I2C is not set
+# CONFIG_BOSCH_BNO055_SERIAL is not set
+# CONFIG_BOUNCE is not set
+CONFIG_BPF=y
+# CONFIG_BPFILTER is not set
+CONFIG_BPF_JIT=y
+# CONFIG_BPF_JIT_ALWAYS_ON is not set
+CONFIG_BPF_JIT_DEFAULT_ON=y
+# CONFIG_BPF_LSM is not set
+# CONFIG_BPF_PRELOAD is not set
+# CONFIG_BPF_STREAM_PARSER is not set
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
+# CONFIG_BPQETHER is not set
+CONFIG_BQL=y
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_BRCMFMAC is not set
+# CONFIG_BRCMSMAC is not set
+# CONFIG_BRCMSTB_GISB_ARB is not set
+# CONFIG_BRCMSTB_L2_IRQ is not set
+CONFIG_BRIDGE=y
+# CONFIG_BRIDGE_CFM is not set
+# CONFIG_BRIDGE_EBT_802_3 is not set
+# CONFIG_BRIDGE_EBT_AMONG is not set
+# CONFIG_BRIDGE_EBT_ARP is not set
+# CONFIG_BRIDGE_EBT_ARPREPLY is not set
+# CONFIG_BRIDGE_EBT_BROUTE is not set
+# CONFIG_BRIDGE_EBT_DNAT is not set
+# CONFIG_BRIDGE_EBT_IP is not set
+# CONFIG_BRIDGE_EBT_IP6 is not set
+# CONFIG_BRIDGE_EBT_LIMIT is not set
+# CONFIG_BRIDGE_EBT_LOG is not set
+# CONFIG_BRIDGE_EBT_MARK is not set
+# CONFIG_BRIDGE_EBT_MARK_T is not set
+# CONFIG_BRIDGE_EBT_NFLOG is not set
+# CONFIG_BRIDGE_EBT_PKTTYPE is not set
+# CONFIG_BRIDGE_EBT_REDIRECT is not set
+# CONFIG_BRIDGE_EBT_SNAT is not set
+# CONFIG_BRIDGE_EBT_STP is not set
+# CONFIG_BRIDGE_EBT_T_FILTER is not set
+# CONFIG_BRIDGE_EBT_T_NAT is not set
+# CONFIG_BRIDGE_EBT_VLAN is not set
+CONFIG_BRIDGE_IGMP_SNOOPING=y
+# CONFIG_BRIDGE_MRP is not set
+# CONFIG_BRIDGE_NETFILTER is not set
+# CONFIG_BRIDGE_NF_EBTABLES is not set
+CONFIG_BRIDGE_VLAN_FILTERING=y
+# CONFIG_BROADCOM_PHY is not set
+CONFIG_BROKEN_ON_SMP=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_BT is not set
+# CONFIG_BTRFS_ASSERT is not set
+# CONFIG_BTRFS_DEBUG is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_BTRFS_FS_CHECK_INTEGRITY is not set
+# CONFIG_BTRFS_FS_POSIX_ACL is not set
+# CONFIG_BTRFS_FS_REF_VERIFY is not set
+# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set
+# CONFIG_BT_AOSPEXT is not set
+# CONFIG_BT_ATH3K is not set
+# CONFIG_BT_BNEP is not set
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+# CONFIG_BT_BREDR is not set
+# CONFIG_BT_CMTP is not set
+# CONFIG_BT_FEATURE_DEBUG is not set
+# CONFIG_BT_HCIBCM203X is not set
+# CONFIG_BT_HCIBCM4377 is not set
+# CONFIG_BT_HCIBFUSB is not set
+# CONFIG_BT_HCIBLUECARD is not set
+# CONFIG_BT_HCIBPA10X is not set
+# CONFIG_BT_HCIBT3C is not set
+# CONFIG_BT_HCIBTSDIO is not set
+# CONFIG_BT_HCIBTUSB is not set
+# CONFIG_BT_HCIBTUSB_AUTOSUSPEND is not set
+# CONFIG_BT_HCIBTUSB_MTK is not set
+CONFIG_BT_HCIBTUSB_POLL_SYNC=y
+# CONFIG_BT_HCIBTUSB_RTL is not set
+# CONFIG_BT_HCIDTL1 is not set
+# CONFIG_BT_HCIUART is not set
+# CONFIG_BT_HCIUART_3WIRE is not set
+# CONFIG_BT_HCIUART_AG6XX is not set
+# CONFIG_BT_HCIUART_ATH3K is not set
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIUART_H4=y
+# CONFIG_BT_HCIUART_LL is not set
+# CONFIG_BT_HCIUART_MRVL is not set
+# CONFIG_BT_HCIUART_QCA is not set
+# CONFIG_BT_HCIUART_RTL is not set
+# CONFIG_BT_HCIVHCI is not set
+# CONFIG_BT_HIDP is not set
+# CONFIG_BT_LE is not set
+# CONFIG_BT_LEDS is not set
+CONFIG_BT_LE_L2CAP_ECRED=y
+# CONFIG_BT_MRVL is not set
+# CONFIG_BT_MSFTEXT is not set
+# CONFIG_BT_MTKSDIO is not set
+# CONFIG_BT_MTKUART is not set
+# CONFIG_BT_NXPUART is not set
+# CONFIG_BT_RFCOMM is not set
+CONFIG_BT_RFCOMM_TTY=y
+# CONFIG_BT_SELFTEST is not set
+# CONFIG_BT_VIRTIO is not set
+CONFIG_BUG=y
+# CONFIG_BUG_ON_DATA_CORRUPTION is not set
+CONFIG_BUILDTIME_TABLE_SORT=y
+CONFIG_BUILD_SALT=""
+# CONFIG_C2PORT is not set
+# CONFIG_CACHESTAT_SYSCALL is not set
+CONFIG_CACHE_L2X0_PMU=y
+# CONFIG_CADENCE_WATCHDOG is not set
+# CONFIG_CAIF is not set
+# CONFIG_CAN is not set
+# CONFIG_CAN_BCM is not set
+# CONFIG_CAN_CAN327 is not set
+# CONFIG_CAN_CTUCANFD_PCI is not set
+# CONFIG_CAN_CTUCANFD_PLATFORM is not set
+# CONFIG_CAN_DEBUG_DEVICES is not set
+# CONFIG_CAN_DEV is not set
+# CONFIG_CAN_ESD_USB is not set
+# CONFIG_CAN_ETAS_ES58X is not set
+# CONFIG_CAN_F81604 is not set
+# CONFIG_CAN_GS_USB is not set
+# CONFIG_CAN_GW is not set
+# CONFIG_CAN_HI311X is not set
+# CONFIG_CAN_IFI_CANFD is not set
+# CONFIG_CAN_ISOTP is not set
+# CONFIG_CAN_J1939 is not set
+# CONFIG_CAN_KVASER_PCIEFD is not set
+# CONFIG_CAN_MCBA_USB is not set
+# CONFIG_CAN_MCP251XFD is not set
+# CONFIG_CAN_M_CAN is not set
+# CONFIG_CAN_NETLINK is not set
+# CONFIG_CAN_PEAK_PCIEFD is not set
+# CONFIG_CAN_RAW is not set
+# CONFIG_CAN_RCAR is not set
+# CONFIG_CAN_RCAR_CANFD is not set
+# CONFIG_CAN_SLCAN is not set
+# CONFIG_CAN_SUN4I is not set
+# CONFIG_CAN_UCAN is not set
+# CONFIG_CAN_VCAN is not set
+# CONFIG_CAN_VXCAN is not set
+# CONFIG_CAPI_TRACE is not set
+CONFIG_CARDBUS=y
+# CONFIG_CARL9170 is not set
+# CONFIG_CASSINI is not set
+# CONFIG_CAVIUM_CPT is not set
+# CONFIG_CAVIUM_ERRATUM_22375 is not set
+# CONFIG_CAVIUM_ERRATUM_23144 is not set
+# CONFIG_CAVIUM_ERRATUM_23154 is not set
+# CONFIG_CAVIUM_ERRATUM_27456 is not set
+# CONFIG_CAVIUM_ERRATUM_30115 is not set
+# CONFIG_CAVIUM_OCTEON_SOC is not set
+# CONFIG_CAVIUM_PTP is not set
+# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set
+# CONFIG_CB710_CORE is not set
+# CONFIG_CC10001_ADC is not set
+# CONFIG_CCS811 is not set
+CONFIG_CC_CAN_LINK=y
+CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_CDX_BUS is not set
+# CONFIG_CEPH_FS is not set
+# CONFIG_CEPH_LIB is not set
+# CONFIG_CFG80211 is not set
+# CONFIG_CFG80211_CERTIFICATION_ONUS is not set
+CONFIG_CFG80211_HEADERS=y
+# CONFIG_CGROUPS is not set
+# CONFIG_CGROUP_FAVOR_DYNMODS is not set
+# CONFIG_CGROUP_MISC is not set
+# CONFIG_CHARGER_ADP5061 is not set
+# CONFIG_CHARGER_BD99954 is not set
+# CONFIG_CHARGER_BQ2415X is not set
+# CONFIG_CHARGER_BQ24190 is not set
+# CONFIG_CHARGER_BQ24257 is not set
+# CONFIG_CHARGER_BQ24735 is not set
+# CONFIG_CHARGER_BQ2515X is not set
+# CONFIG_CHARGER_BQ256XX is not set
+# CONFIG_CHARGER_BQ25890 is not set
+# CONFIG_CHARGER_BQ25980 is not set
+# CONFIG_CHARGER_DETECTOR_MAX14656 is not set
+# CONFIG_CHARGER_GPIO is not set
+# CONFIG_CHARGER_ISP1704 is not set
+# CONFIG_CHARGER_LP8727 is not set
+# CONFIG_CHARGER_LT3651 is not set
+# CONFIG_CHARGER_LTC4162L is not set
+# CONFIG_CHARGER_MANAGER is not set
+# CONFIG_CHARGER_MAX77976 is not set
+# CONFIG_CHARGER_MAX8903 is not set
+# CONFIG_CHARGER_QCOM_SMB2 is not set
+# CONFIG_CHARGER_QCOM_SMBB is not set
+# CONFIG_CHARGER_RT9455 is not set
+# CONFIG_CHARGER_RT9467 is not set
+# CONFIG_CHARGER_RT9471 is not set
+# CONFIG_CHARGER_SBS is not set
+# CONFIG_CHARGER_SMB347 is not set
+# CONFIG_CHARGER_TWL4030 is not set
+# CONFIG_CHARGER_UCS1002 is not set
+# CONFIG_CHECKPOINT_RESTORE is not set
+# CONFIG_CHELSIO_T1 is not set
+# CONFIG_CHELSIO_T3 is not set
+# CONFIG_CHELSIO_T4 is not set
+# CONFIG_CHELSIO_T4VF is not set
+# CONFIG_CHROME_PLATFORMS is not set
+# CONFIG_CHR_DEV_SCH is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_CIFS is not set
+CONFIG_CIFS_ALLOW_INSECURE_LEGACY=y
+# CONFIG_CIFS_DEBUG is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_FSCACHE is not set
+# CONFIG_CIFS_NFSD_EXPORT is not set
+CONFIG_CIFS_POSIX=y
+# CONFIG_CIFS_STATS2 is not set
+# CONFIG_CIFS_SWN_UPCALL is not set
+CONFIG_CIFS_XATTR=y
+# CONFIG_CIO_DAC is not set
+# CONFIG_CLKSRC_PISTACHIO is not set
+# CONFIG_CLKSRC_VERSATILE is not set
+# CONFIG_CLK_GFM_LPASS_SM8250 is not set
+# CONFIG_CLK_HSDK is not set
+# CONFIG_CLK_ICST is not set
+# CONFIG_CLK_QORIQ is not set
+# CONFIG_CLK_SP810 is not set
+CONFIG_CLS_U32_MARK=y
+# CONFIG_CLS_U32_PERF is not set
+# CONFIG_CM32181 is not set
+# CONFIG_CM3232 is not set
+# CONFIG_CM3323 is not set
+# CONFIG_CM3605 is not set
+# CONFIG_CM36651 is not set
+# CONFIG_CMA is not set
+CONFIG_CMDLINE=""
+# CONFIG_CMDLINE_BOOL is not set
+# CONFIG_CMDLINE_EXTEND is not set
+# CONFIG_CMDLINE_FORCE is not set
+# CONFIG_CMDLINE_FROM_BOOTLOADER is not set
+# CONFIG_CMDLINE_PARTITION is not set
+# CONFIG_CNIC is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_CODE_PATCHING_SELFTEST is not set
+# CONFIG_COMEDI is not set
+# CONFIG_COMMON_CLK_AXI_CLKGEN is not set
+# CONFIG_COMMON_CLK_BOSTON is not set
+# CONFIG_COMMON_CLK_CDCE706 is not set
+# CONFIG_COMMON_CLK_CDCE925 is not set
+# CONFIG_COMMON_CLK_CS2000_CP is not set
+# CONFIG_COMMON_CLK_FIXED_MMIO is not set
+# CONFIG_COMMON_CLK_IPROC is not set
+# CONFIG_COMMON_CLK_MAX9485 is not set
+# CONFIG_COMMON_CLK_MEDIATEK_FHCTL is not set
+# CONFIG_COMMON_CLK_MT6765 is not set
+# CONFIG_COMMON_CLK_MT8167 is not set
+# CONFIG_COMMON_CLK_MT8167_AUDSYS is not set
+# CONFIG_COMMON_CLK_MT8167_IMGSYS is not set
+# CONFIG_COMMON_CLK_MT8167_MFGCFG is not set
+# CONFIG_COMMON_CLK_MT8167_MMSYS is not set
+# CONFIG_COMMON_CLK_MT8167_VDECSYS is not set
+# CONFIG_COMMON_CLK_MT8188 is not set
+# CONFIG_COMMON_CLK_MT8192 is not set
+# CONFIG_COMMON_CLK_NXP is not set
+# CONFIG_COMMON_CLK_PIC32 is not set
+# CONFIG_COMMON_CLK_PISTACHIO is not set
+# CONFIG_COMMON_CLK_PWM is not set
+# CONFIG_COMMON_CLK_PXA is not set
+# CONFIG_COMMON_CLK_QCOM is not set
+# CONFIG_COMMON_CLK_RS9_PCIE is not set
+# CONFIG_COMMON_CLK_SI514 is not set
+# CONFIG_COMMON_CLK_SI521XX is not set
+# CONFIG_COMMON_CLK_SI5341 is not set
+# CONFIG_COMMON_CLK_SI5351 is not set
+# CONFIG_COMMON_CLK_SI544 is not set
+# CONFIG_COMMON_CLK_SI570 is not set
+# CONFIG_COMMON_CLK_VC3 is not set
+# CONFIG_COMMON_CLK_VC5 is not set
+# CONFIG_COMMON_CLK_VC7 is not set
+# CONFIG_COMMON_CLK_XGENE is not set
+# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set
+CONFIG_COMPACTION=y
+# CONFIG_COMPAL_LAPTOP is not set
+# CONFIG_COMPAT is not set
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_COMPILE_TEST is not set
+# CONFIG_CONFIGFS_FS is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
+CONFIG_CONSOLE_LOGLEVEL_QUIET=4
+CONFIG_CONSTRUCTORS=y
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_COPS is not set
+# CONFIG_CORDIC is not set
+# CONFIG_COREDUMP is not set
+# CONFIG_CORESIGHT is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_CORTINA_PHY is not set
+# CONFIG_COUNTER is not set
+# CONFIG_CPA_DEBUG is not set
+# CONFIG_CPUFREQ_DT is not set
+# CONFIG_CPUFREQ_DT_PLATDEV is not set
+# CONFIG_CPU_BIG_ENDIAN is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_GOV_SCHEDUTIL is not set
+# CONFIG_CPU_FREQ_THERMAL is not set
+# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_ICACHE_MISMATCH_WORKAROUND is not set
+# CONFIG_CPU_IDLE is not set
+# CONFIG_CPU_IDLE_GOV_LADDER is not set
+# CONFIG_CPU_IDLE_GOV_MENU is not set
+# CONFIG_CPU_IDLE_GOV_TEO is not set
+# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set
+# CONFIG_CPU_ISOLATION is not set
+# CONFIG_CPU_LITTLE_ENDIAN is not set
+# CONFIG_CPU_NO_EFFICIENT_FFS is not set
+CONFIG_CPU_SW_DOMAIN_PAN=y
+# CONFIG_CPU_THERMAL is not set
+# CONFIG_CRAMFS is not set
+CONFIG_CRAMFS_BLOCKDEV=y
+# CONFIG_CRAMFS_MTD is not set
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=y
+# CONFIG_CRC32_BIT is not set
+# CONFIG_CRC32_SARWATE is not set
+# CONFIG_CRC32_SELFTEST is not set
+# CONFIG_CRC32_SLICEBY4 is not set
+CONFIG_CRC32_SLICEBY8=y
+# CONFIG_CRC4 is not set
+# CONFIG_CRC64 is not set
+# CONFIG_CRC64_ROCKSOFT is not set
+# CONFIG_CRC7 is not set
+# CONFIG_CRC8 is not set
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC_ITU_T is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+# CONFIG_CROS_HPS_I2C is not set
+CONFIG_CRYPTO=y
+# CONFIG_CRYPTO_842 is not set
+CONFIG_CRYPTO_ACOMP2=y
+# CONFIG_CRYPTO_ADIANTUM is not set
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+# CONFIG_CRYPTO_AEGIS128 is not set
+# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_ARM is not set
+# CONFIG_CRYPTO_AES_ARM64 is not set
+# CONFIG_CRYPTO_AES_ARM64_BS is not set
+# CONFIG_CRYPTO_AES_ARM64_CE is not set
+# CONFIG_CRYPTO_AES_ARM64_CE_BLK is not set
+# CONFIG_CRYPTO_AES_ARM64_CE_CCM is not set
+# CONFIG_CRYPTO_AES_ARM64_NEON_BLK is not set
+# CONFIG_CRYPTO_AES_ARM_BS is not set
+# CONFIG_CRYPTO_AES_ARM_CE is not set
+# CONFIG_CRYPTO_AES_NI_INTEL is not set
+# CONFIG_CRYPTO_AES_TI is not set
+CONFIG_CRYPTO_AKCIPHER=y
+CONFIG_CRYPTO_AKCIPHER2=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_ARIA is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_BLAKE2B is not set
+# CONFIG_CRYPTO_BLAKE2B_NEON is not set
+# CONFIG_CRYPTO_BLAKE2S_ARM is not set
+# CONFIG_CRYPTO_BLAKE2S_X86 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_CBC is not set
+CONFIG_CRYPTO_CCM=y
+# CONFIG_CRYPTO_CFB is not set
+# CONFIG_CRYPTO_CHACHA20 is not set
+# CONFIG_CRYPTO_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_CHACHA20_NEON is not set
+# CONFIG_CRYPTO_CHACHA20_X86_64 is not set
+# CONFIG_CRYPTO_CHACHA_MIPS is not set
+# CONFIG_CRYPTO_CMAC is not set
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CRC32C_INTEL is not set
+# CONFIG_CRYPTO_CRC32_ARM_CE is not set
+# CONFIG_CRYPTO_CRCT10DIF is not set
+# CONFIG_CRYPTO_CRCT10DIF_ARM64_CE is not set
+# CONFIG_CRYPTO_CRCT10DIF_ARM_CE is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_CTR=y
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_CURVE25519 is not set
+# CONFIG_CRYPTO_CURVE25519_NEON is not set
+# CONFIG_CRYPTO_CURVE25519_X86 is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set
+# CONFIG_CRYPTO_DEV_ATMEL_AES is not set
+# CONFIG_CRYPTO_DEV_ATMEL_AUTHENC is not set
+# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set
+# CONFIG_CRYPTO_DEV_ATMEL_SHA is not set
+# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set
+# CONFIG_CRYPTO_DEV_ATMEL_TDES is not set
+# CONFIG_CRYPTO_DEV_CAVIUM_ZIP is not set
+# CONFIG_CRYPTO_DEV_CCP is not set
+# CONFIG_CRYPTO_DEV_CCP_DEBUGFS is not set
+# CONFIG_CRYPTO_DEV_CCREE is not set
+# CONFIG_CRYPTO_DEV_FSL_CAAM is not set
+# CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC is not set
+# CONFIG_CRYPTO_DEV_FSL_CAAM_DEBUG is not set
+# CONFIG_CRYPTO_DEV_FSL_CAAM_INTC is not set
+# CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST is not set
+# CONFIG_CRYPTO_DEV_HIFN_795X is not set
+# CONFIG_CRYPTO_DEV_HISI_SEC is not set
+# CONFIG_CRYPTO_DEV_HISI_ZIP is not set
+# CONFIG_CRYPTO_DEV_IMGTEC_HASH is not set
+# CONFIG_CRYPTO_DEV_MARVELL_CESA is not set
+# CONFIG_CRYPTO_DEV_MXS_DCP is not set
+# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set
+# CONFIG_CRYPTO_DEV_OCTEONTX_CPT is not set
+# CONFIG_CRYPTO_DEV_QAT_4XXX is not set
+# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set
+# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set
+# CONFIG_CRYPTO_DEV_QAT_C62X is not set
+# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set
+# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set
+# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set
+# CONFIG_CRYPTO_DEV_QCE is not set
+# CONFIG_CRYPTO_DEV_S5P is not set
+# CONFIG_CRYPTO_DEV_SAFEXCEL is not set
+# CONFIG_CRYPTO_DEV_SAHARA is not set
+# CONFIG_CRYPTO_DEV_SP_PSP is not set
+# CONFIG_CRYPTO_DEV_TALITOS is not set
+# CONFIG_CRYPTO_DEV_VIRTIO is not set
+# CONFIG_CRYPTO_DH is not set
+# CONFIG_CRYPTO_DRBG_CTR is not set
+# CONFIG_CRYPTO_DRBG_HASH is not set
+# CONFIG_CRYPTO_DRBG_MENU is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_ECDH is not set
+# CONFIG_CRYPTO_ECDSA is not set
+# CONFIG_CRYPTO_ECHAINIV is not set
+# CONFIG_CRYPTO_ECRDSA is not set
+# CONFIG_CRYPTO_ESSIV is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_GCM=y
+CONFIG_CRYPTO_GHASH=y
+# CONFIG_CRYPTO_GHASH_ARM64_CE is not set
+# CONFIG_CRYPTO_GHASH_ARM_CE is not set
+# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+# CONFIG_CRYPTO_HCTR2 is not set
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_CRYPTO_JITTERENTROPY is not set
+# CONFIG_CRYPTO_JITTERENTROPY_TESTINTERFACE is not set
+# CONFIG_CRYPTO_KEYWRAP is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+CONFIG_CRYPTO_KPP=y
+CONFIG_CRYPTO_KPP2=y
+CONFIG_CRYPTO_LIB_AES=y
+CONFIG_CRYPTO_LIB_ARC4=y
+# CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC is not set
+# CONFIG_CRYPTO_LIB_CHACHA is not set
+# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_LIB_CURVE25519 is not set
+# CONFIG_CRYPTO_LIB_POLY1305 is not set
+CONFIG_CRYPTO_LIB_POLY1305_RSIZE=9
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_LZ4 is not set
+# CONFIG_CRYPTO_LZ4HC is not set
+# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_NHPOLY1305_NEON is not set
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_NULL2=y
+# CONFIG_CRYPTO_OFB is not set
+# CONFIG_CRYPTO_PCBC is not set
+CONFIG_CRYPTO_PCRYPT=y
+# CONFIG_CRYPTO_POLY1305 is not set
+# CONFIG_CRYPTO_POLY1305_ARM is not set
+# CONFIG_CRYPTO_POLY1305_MIPS is not set
+# CONFIG_CRYPTO_POLY1305_NEON is not set
+# CONFIG_CRYPTO_POLY1305_X86_64 is not set
+# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RNG is not set
+# CONFIG_CRYPTO_RSA is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SEQIV is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA1_ARM is not set
+# CONFIG_CRYPTO_SHA1_ARM64_CE is not set
+# CONFIG_CRYPTO_SHA1_ARM_CE is not set
+# CONFIG_CRYPTO_SHA1_ARM_NEON is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA256_ARM is not set
+# CONFIG_CRYPTO_SHA256_ARM64 is not set
+# CONFIG_CRYPTO_SHA2_ARM64_CE is not set
+# CONFIG_CRYPTO_SHA2_ARM_CE is not set
+# CONFIG_CRYPTO_SHA3 is not set
+# CONFIG_CRYPTO_SHA3_ARM64 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_SHA512_ARM is not set
+# CONFIG_CRYPTO_SHA512_ARM64 is not set
+# CONFIG_CRYPTO_SHA512_ARM64_CE is not set
+# CONFIG_CRYPTO_SIMD is not set
+CONFIG_CRYPTO_SKCIPHER=y
+CONFIG_CRYPTO_SKCIPHER2=y
+# CONFIG_CRYPTO_SM2 is not set
+# CONFIG_CRYPTO_SM3 is not set
+# CONFIG_CRYPTO_SM3_ARM64_CE is not set
+# CONFIG_CRYPTO_SM3_GENERIC is not set
+# CONFIG_CRYPTO_SM3_NEON is not set
+# CONFIG_CRYPTO_SM4 is not set
+# CONFIG_CRYPTO_SM4_ARM64_CE is not set
+# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set
+# CONFIG_CRYPTO_SM4_ARM64_CE_CCM is not set
+# CONFIG_CRYPTO_SM4_ARM64_CE_GCM is not set
+# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set
+# CONFIG_CRYPTO_SM4_GENERIC is not set
+# CONFIG_CRYPTO_STATS is not set
+# CONFIG_CRYPTO_STREEBOG is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_TWOFISH_586 is not set
+# CONFIG_CRYPTO_TWOFISH_COMMON is not set
+# CONFIG_CRYPTO_USER is not set
+# CONFIG_CRYPTO_USER_API_AEAD is not set
+# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_RNG is not set
+# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+# CONFIG_CRYPTO_VMAC is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_XXHASH is not set
+# CONFIG_CRYPTO_ZSTD is not set
+# CONFIG_CS5535_MFGPT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_CS89x0_PLATFORM is not set
+# CONFIG_CSD_LOCK_WAIT_DEBUG is not set
+# CONFIG_CUSE is not set
+# CONFIG_CW1200 is not set
+# CONFIG_CXD2880_SPI_DRV is not set
+# CONFIG_CXL_BASE is not set
+# CONFIG_CXL_BUS is not set
+# CONFIG_CYPRESS_FIRMWARE is not set
+# CONFIG_CZNIC_PLATFORMS is not set
+# CONFIG_DA280 is not set
+# CONFIG_DA311 is not set
+# CONFIG_DAMON is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_DAX is not set
+# CONFIG_DCB is not set
+# CONFIG_DDR is not set
+# CONFIG_DEBUG_ALIGN_RODATA is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_CGROUP_REF is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_EFI is not set
+# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_FS_ALLOW_ALL=y
+# CONFIG_DEBUG_FS_ALLOW_NONE is not set
+# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_DEBUG_HIGHMEM is not set
+# CONFIG_DEBUG_ICEDCC is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_INFO_BTF is not set
+CONFIG_DEBUG_INFO_COMPRESSED_NONE=y
+# CONFIG_DEBUG_INFO_COMPRESSED_ZLIB is not set
+# CONFIG_DEBUG_INFO_COMPRESSED_ZSTD is not set
+# CONFIG_DEBUG_INFO_DWARF4 is not set
+# CONFIG_DEBUG_INFO_DWARF5 is not set
+CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
+# CONFIG_DEBUG_INFO_NONE is not set
+# CONFIG_DEBUG_INFO_REDUCED is not set
+# CONFIG_DEBUG_INFO_SPLIT is not set
+# CONFIG_DEBUG_IRQFLAGS is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_KMAP_LOCAL is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_KOBJECT_RELEASE is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_LL is not set
+# CONFIG_DEBUG_LL_UART_8250 is not set
+# CONFIG_DEBUG_LL_UART_PL01X is not set
+# CONFIG_DEBUG_LOCKDEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_DEBUG_MAPLE_TREE is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_MISC is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_NET is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_DEBUG_PAGE_REF is not set
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_DEBUG_PINCTRL is not set
+# CONFIG_DEBUG_PLIST is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RODATA_TEST is not set
+# CONFIG_DEBUG_RSEQ is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_DEBUG_RWSEMS is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+# CONFIG_DEBUG_SEMIHOSTING is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set
+# CONFIG_DEBUG_TIMEKEEPING is not set
+# CONFIG_DEBUG_UART_8250_PALMCHIP is not set
+# CONFIG_DEBUG_UART_8250_WORD is not set
+# CONFIG_DEBUG_UART_FLOW_CONTROL is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_VIRTUAL is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_VM_MAPLE_TREE is not set
+# CONFIG_DEBUG_VM_PGFLAGS is not set
+# CONFIG_DEBUG_VM_PGTABLE is not set
+# CONFIG_DEBUG_VM_RB is not set
+# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set
+# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set
+# CONFIG_DEBUG_WX is not set
+# CONFIG_DEBUG_ZBOOT is not set
+# CONFIG_DEFAULT_CODEL is not set
+CONFIG_DEFAULT_CUBIC=y
+# CONFIG_DEFAULT_FQ is not set
+CONFIG_DEFAULT_FQ_CODEL=y
+# CONFIG_DEFAULT_FQ_PIE is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+CONFIG_DEFAULT_INIT=""
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_DEFAULT_NET_SCH="fq_codel"
+# CONFIG_DEFAULT_PFIFO_FAST is not set
+# CONFIG_DEFAULT_RENO is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+# CONFIG_DEFAULT_SECURITY_SELINUX is not set
+# CONFIG_DEFAULT_SFQ is not set
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
+# CONFIG_DELL_LAPTOP is not set
+# CONFIG_DELL_RBTN is not set
+# CONFIG_DELL_SMBIOS is not set
+# CONFIG_DELL_SMO8800 is not set
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+# CONFIG_DETECT_HUNG_TASK is not set
+# CONFIG_DEVMEM is not set
+CONFIG_DEVPORT=y
+# CONFIG_DEVTMPFS is not set
+# CONFIG_DEVTMPFS_MOUNT is not set
+# CONFIG_DEVTMPFS_SAFE is not set
+# CONFIG_DEV_DAX is not set
+# CONFIG_DHT11 is not set
+# CONFIG_DL2K is not set
+# CONFIG_DLHL60D is not set
+# CONFIG_DLM is not set
+# CONFIG_DM9000 is not set
+# CONFIG_DM9051 is not set
+# CONFIG_DMABUF_DEBUG is not set
+# CONFIG_DMABUF_HEAPS is not set
+# CONFIG_DMABUF_MOVE_NOTIFY is not set
+# CONFIG_DMABUF_SELFTESTS is not set
+# CONFIG_DMABUF_SYSFS_STATS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_DMADEVICES_DEBUG is not set
+# CONFIG_DMAPOOL_TEST is not set
+# CONFIG_DMARD06 is not set
+# CONFIG_DMARD09 is not set
+# CONFIG_DMARD10 is not set
+# CONFIG_DMATEST is not set
+# CONFIG_DMA_API_DEBUG is not set
+CONFIG_DMA_COHERENT_POOL=y
+CONFIG_DMA_DECLARE_COHERENT=y
+# CONFIG_DMA_ENGINE is not set
+# CONFIG_DMA_FENCE_TRACE is not set
+# CONFIG_DMA_JZ4780 is not set
+# CONFIG_DMA_MAP_BENCHMARK is not set
+CONFIG_DMA_NONCOHERENT_MMAP=y
+# CONFIG_DMA_RESTRICTED_POOL is not set
+# CONFIG_DMA_SHARED_BUFFER is not set
+# CONFIG_DM_AUDIT is not set
+# CONFIG_DM_CACHE is not set
+# CONFIG_DM_CLONE is not set
+# CONFIG_DM_DEBUG is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_DUST is not set
+# CONFIG_DM_EBS is not set
+# CONFIG_DM_ERA is not set
+# CONFIG_DM_FLAKEY is not set
+# CONFIG_DM_INTEGRITY is not set
+# CONFIG_DM_LOG_USERSPACE is not set
+# CONFIG_DM_LOG_WRITES is not set
+# CONFIG_DM_MULTIPATH is not set
+# CONFIG_DM_RAID is not set
+# CONFIG_DM_SWITCH is not set
+# CONFIG_DM_THIN_PROVISIONING is not set
+# CONFIG_DM_UEVENT is not set
+# CONFIG_DM_UNSTRIPED is not set
+# CONFIG_DM_VERITY is not set
+# CONFIG_DM_WRITECACHE is not set
+# CONFIG_DM_ZERO is not set
+# CONFIG_DM_ZONED is not set
+# CONFIG_DNET is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_DNS_RESOLVER is not set
+# CONFIG_DP83640_PHY is not set
+# CONFIG_DP83822_PHY is not set
+# CONFIG_DP83848_PHY is not set
+# CONFIG_DP83867_PHY is not set
+# CONFIG_DP83869_PHY is not set
+# CONFIG_DP83TC811_PHY is not set
+# CONFIG_DP83TD510_PHY is not set
+# CONFIG_DPM_WATCHDOG is not set
+# CONFIG_DPOT_DAC is not set
+# CONFIG_DPS310 is not set
+CONFIG_DQL=y
+# CONFIG_DRAGONRISE_FF is not set
+# CONFIG_DRM is not set
+# CONFIG_DRM_ACCEL is not set
+# CONFIG_DRM_AMDGPU is not set
+# CONFIG_DRM_AMDGPU_CIK is not set
+# CONFIG_DRM_AMDGPU_SI is not set
+# CONFIG_DRM_AMDGPU_USERPTR is not set
+# CONFIG_DRM_AMDGPU_WERROR is not set
+# CONFIG_DRM_AMD_ACP is not set
+# CONFIG_DRM_AMD_DC_SI is not set
+# CONFIG_DRM_AMD_SECURE_DISPLAY is not set
+# CONFIG_DRM_ANALOGIX_ANX6345 is not set
+# CONFIG_DRM_ANALOGIX_ANX7625 is not set
+# CONFIG_DRM_ANALOGIX_ANX78XX is not set
+# CONFIG_DRM_ARCPGU is not set
+# CONFIG_DRM_ARMADA is not set
+# CONFIG_DRM_AST is not set
+# CONFIG_DRM_ATMEL_HLCDC is not set
+# CONFIG_DRM_BOCHS is not set
+# CONFIG_DRM_CDNS_DSI is not set
+# CONFIG_DRM_CDNS_MHDP8546 is not set
+# CONFIG_DRM_CHIPONE_ICN6211 is not set
+# CONFIG_DRM_CHRONTEL_CH7033 is not set
+# CONFIG_DRM_CIRRUS_QEMU is not set
+# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set
+# CONFIG_DRM_DEBUG_MM is not set
+# CONFIG_DRM_DEBUG_MODESET_LOCK is not set
+# CONFIG_DRM_DISPLAY_CONNECTOR is not set
+# CONFIG_DRM_DP_AUX_CHARDEV is not set
+# CONFIG_DRM_DP_CEC is not set
+# CONFIG_DRM_DW_HDMI_CEC is not set
+# CONFIG_DRM_ETNAVIV is not set
+# CONFIG_DRM_EXYNOS is not set
+# CONFIG_DRM_FBDEV_EMULATION is not set
+# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set
+# CONFIG_DRM_FSL_DCU is not set
+# CONFIG_DRM_GM12U320 is not set
+# CONFIG_DRM_GMA500 is not set
+# CONFIG_DRM_GUD is not set
+# CONFIG_DRM_HDLCD is not set
+# CONFIG_DRM_HISI_HIBMC is not set
+# CONFIG_DRM_HISI_KIRIN is not set
+# CONFIG_DRM_I2C_ADV7511 is not set
+# CONFIG_DRM_I2C_CH7006 is not set
+# CONFIG_DRM_I2C_NXP_TDA9950 is not set
+# CONFIG_DRM_I2C_NXP_TDA998X is not set
+# CONFIG_DRM_I2C_SIL164 is not set
+# CONFIG_DRM_I915 is not set
+# CONFIG_DRM_IMX_LCDIF is not set
+# CONFIG_DRM_ITE_IT6505 is not set
+# CONFIG_DRM_ITE_IT66121 is not set
+# CONFIG_DRM_KOMEDA is not set
+# CONFIG_DRM_LEGACY is not set
+# CONFIG_DRM_LIB_RANDOM is not set
+# CONFIG_DRM_LIMA is not set
+# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set
+# CONFIG_DRM_LOGICVC is not set
+# CONFIG_DRM_LONTIUM_LT8912B is not set
+# CONFIG_DRM_LONTIUM_LT9211 is not set
+# CONFIG_DRM_LONTIUM_LT9611 is not set
+# CONFIG_DRM_LONTIUM_LT9611UXC is not set
+# CONFIG_DRM_LOONGSON is not set
+# CONFIG_DRM_LVDS_CODEC is not set
+# CONFIG_DRM_MALI_DISPLAY is not set
+# CONFIG_DRM_MCDE is not set
+# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set
+# CONFIG_DRM_MGAG200 is not set
+# CONFIG_DRM_MXSFB is not set
+# CONFIG_DRM_NOUVEAU is not set
+# CONFIG_DRM_NWL_MIPI_DSI is not set
+# CONFIG_DRM_NXP_PTN3460 is not set
+# CONFIG_DRM_OFDRM is not set
+# CONFIG_DRM_OMAP is not set
+# CONFIG_DRM_PANEL_ABT_Y030XX067A is not set
+# CONFIG_DRM_PANEL_ARM_VERSATILE is not set
+# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set
+# CONFIG_DRM_PANEL_AUO_A030JTN01 is not set
+# CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0 is not set
+# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set
+# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set
+# CONFIG_DRM_PANEL_DSI_CM is not set
+# CONFIG_DRM_PANEL_EBBG_FT8719 is not set
+# CONFIG_DRM_PANEL_EDP is not set
+# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set
+# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set
+# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set
+# CONFIG_DRM_PANEL_HIMAX_HX8394 is not set
+# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9341 is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set
+# CONFIG_DRM_PANEL_INNOLUX_EJ030NA is not set
+# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set
+# CONFIG_DRM_PANEL_JADARD_JD9365DA_H3 is not set
+# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set
+# CONFIG_DRM_PANEL_JDI_R63452 is not set
+# CONFIG_DRM_PANEL_KHADAS_TS050 is not set
+# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set
+# CONFIG_DRM_PANEL_LG_LB035Q02 is not set
+# CONFIG_DRM_PANEL_LG_LG4573 is not set
+# CONFIG_DRM_PANEL_LVDS is not set
+# CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966 is not set
+# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set
+# CONFIG_DRM_PANEL_MIPI_DBI is not set
+# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set
+# CONFIG_DRM_PANEL_NEWVISION_NV3051D is not set
+# CONFIG_DRM_PANEL_NEWVISION_NV3052C is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT35560 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT35950 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT36523 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT36672A is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set
+# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set
+# CONFIG_DRM_PANEL_ORISETECH_OTA5601A is not set
+# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set
+# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set
+# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set
+# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set
+# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_DB7430 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6D27A1 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6D7AA0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_SOFEF00 is not set
+# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set
+# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS060T1SX01 is not set
+# CONFIG_DRM_PANEL_SIMPLE is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set
+# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set
+# CONFIG_DRM_PANEL_SONY_TD4353_JDI is not set
+# CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521 is not set
+# CONFIG_DRM_PANEL_STARTEK_KD070FHFID015 is not set
+# CONFIG_DRM_PANEL_TDO_TL070WSH30 is not set
+# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set
+# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_DRM_PANEL_TPO_TPG110 is not set
+# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set
+# CONFIG_DRM_PANEL_VISIONOX_R66451 is not set
+# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set
+# CONFIG_DRM_PANEL_VISIONOX_VTDR6130 is not set
+# CONFIG_DRM_PANEL_WIDECHIPS_WS2401 is not set
+# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set
+# CONFIG_DRM_PANFROST is not set
+# CONFIG_DRM_PARADE_PS8622 is not set
+# CONFIG_DRM_PARADE_PS8640 is not set
+# CONFIG_DRM_PL111 is not set
+# CONFIG_DRM_QXL is not set
+# CONFIG_DRM_RADEON is not set
+# CONFIG_DRM_RADEON_USERPTR is not set
+# CONFIG_DRM_RCAR_DW_HDMI is not set
+# CONFIG_DRM_RCAR_LVDS is not set
+# CONFIG_DRM_RCAR_USE_LVDS is not set
+# CONFIG_DRM_RCAR_USE_MIPI_DSI is not set
+# CONFIG_DRM_ROCKCHIP is not set
+# CONFIG_DRM_SAMSUNG_DSIM is not set
+# CONFIG_DRM_SII902X is not set
+# CONFIG_DRM_SII9234 is not set
+# CONFIG_DRM_SIL_SII8620 is not set
+# CONFIG_DRM_SIMPLEDRM is not set
+# CONFIG_DRM_SIMPLE_BRIDGE is not set
+# CONFIG_DRM_SSD130X is not set
+# CONFIG_DRM_STI is not set
+# CONFIG_DRM_STM is not set
+# CONFIG_DRM_SUN4I is not set
+# CONFIG_DRM_THINE_THC63LVD1024 is not set
+# CONFIG_DRM_TIDSS is not set
+# CONFIG_DRM_TILCDC is not set
+# CONFIG_DRM_TI_DLPC3433 is not set
+# CONFIG_DRM_TI_SN65DSI83 is not set
+# CONFIG_DRM_TI_SN65DSI86 is not set
+# CONFIG_DRM_TI_TFP410 is not set
+# CONFIG_DRM_TI_TPD12S015 is not set
+# CONFIG_DRM_TOSHIBA_TC358762 is not set
+# CONFIG_DRM_TOSHIBA_TC358764 is not set
+# CONFIG_DRM_TOSHIBA_TC358767 is not set
+# CONFIG_DRM_TOSHIBA_TC358768 is not set
+# CONFIG_DRM_TOSHIBA_TC358775 is not set
+# CONFIG_DRM_TVE200 is not set
+# CONFIG_DRM_UDL is not set
+# CONFIG_DRM_V3D is not set
+# CONFIG_DRM_VBOXVIDEO is not set
+# CONFIG_DRM_VC4_HDMI_CEC is not set
+# CONFIG_DRM_VGEM is not set
+# CONFIG_DRM_VIRTIO_GPU is not set
+# CONFIG_DRM_VKMS is not set
+# CONFIG_DRM_VMWGFX is not set
+# CONFIG_DRM_XEN is not set
+# CONFIG_DRM_XEN_FRONTEND is not set
+# CONFIG_DS1682 is not set
+# CONFIG_DS1803 is not set
+# CONFIG_DS4424 is not set
+# CONFIG_DST_CACHE is not set
+# CONFIG_DTLK is not set
+# CONFIG_DUMMY is not set
+CONFIG_DUMMY_CONSOLE_COLUMNS=80
+CONFIG_DUMMY_CONSOLE_ROWS=25
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_DVB_A8293 is not set
+# CONFIG_DVB_AF9013 is not set
+# CONFIG_DVB_AF9033 is not set
+# CONFIG_DVB_AS102 is not set
+# CONFIG_DVB_ASCOT2E is not set
+# CONFIG_DVB_ATBM8830 is not set
+# CONFIG_DVB_AU8522_DTV is not set
+# CONFIG_DVB_AU8522_V4L is not set
+# CONFIG_DVB_B2C2_FLEXCOP_PCI is not set
+# CONFIG_DVB_B2C2_FLEXCOP_USB is not set
+# CONFIG_DVB_BCM3510 is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_DVB_CX22700 is not set
+# CONFIG_DVB_CX22702 is not set
+# CONFIG_DVB_CX24110 is not set
+# CONFIG_DVB_CX24116 is not set
+# CONFIG_DVB_CX24117 is not set
+# CONFIG_DVB_CX24120 is not set
+# CONFIG_DVB_CX24123 is not set
+# CONFIG_DVB_CXD2099 is not set
+# CONFIG_DVB_CXD2820R is not set
+# CONFIG_DVB_CXD2841ER is not set
+# CONFIG_DVB_CXD2880 is not set
+# CONFIG_DVB_DDBRIDGE is not set
+# CONFIG_DVB_DEMUX_SECTION_LOSS_LOG is not set
+# CONFIG_DVB_DIB3000MB is not set
+# CONFIG_DVB_DIB3000MC is not set
+# CONFIG_DVB_DIB7000M is not set
+# CONFIG_DVB_DIB7000P is not set
+# CONFIG_DVB_DIB8000 is not set
+# CONFIG_DVB_DIB9000 is not set
+# CONFIG_DVB_DRX39XYJ is not set
+# CONFIG_DVB_DRXD is not set
+# CONFIG_DVB_DRXK is not set
+# CONFIG_DVB_DS3000 is not set
+# CONFIG_DVB_DUMMY_FE is not set
+# CONFIG_DVB_DYNAMIC_MINORS is not set
+# CONFIG_DVB_EC100 is not set
+# CONFIG_DVB_FIREDTV is not set
+# CONFIG_DVB_HELENE is not set
+# CONFIG_DVB_HORUS3A is not set
+# CONFIG_DVB_ISL6405 is not set
+# CONFIG_DVB_ISL6421 is not set
+# CONFIG_DVB_ISL6423 is not set
+# CONFIG_DVB_IX2505V is not set
+# CONFIG_DVB_L64781 is not set
+# CONFIG_DVB_LG2160 is not set
+# CONFIG_DVB_LGDT3305 is not set
+# CONFIG_DVB_LGDT3306A is not set
+# CONFIG_DVB_LGDT330X is not set
+# CONFIG_DVB_LGS8GL5 is not set
+# CONFIG_DVB_LGS8GXX is not set
+# CONFIG_DVB_LNBH25 is not set
+# CONFIG_DVB_LNBH29 is not set
+# CONFIG_DVB_LNBP21 is not set
+# CONFIG_DVB_LNBP22 is not set
+# CONFIG_DVB_M88DS3103 is not set
+# CONFIG_DVB_M88RS2000 is not set
+CONFIG_DVB_MAX_ADAPTERS=16
+# CONFIG_DVB_MB86A16 is not set
+# CONFIG_DVB_MB86A20S is not set
+# CONFIG_DVB_MMAP is not set
+# CONFIG_DVB_MN88443X is not set
+# CONFIG_DVB_MN88472 is not set
+# CONFIG_DVB_MN88473 is not set
+# CONFIG_DVB_MT312 is not set
+# CONFIG_DVB_MT352 is not set
+# CONFIG_DVB_MXL5XX is not set
+# CONFIG_DVB_MXL692 is not set
+# CONFIG_DVB_NET is not set
+# CONFIG_DVB_NETUP_UNIDVB is not set
+# CONFIG_DVB_NGENE is not set
+# CONFIG_DVB_NXT200X is not set
+# CONFIG_DVB_NXT6000 is not set
+# CONFIG_DVB_OR51132 is not set
+# CONFIG_DVB_OR51211 is not set
+# CONFIG_DVB_PLATFORM_DRIVERS is not set
+# CONFIG_DVB_PLL is not set
+# CONFIG_DVB_PLUTO2 is not set
+# CONFIG_DVB_PT1 is not set
+# CONFIG_DVB_PT3 is not set
+# CONFIG_DVB_RTL2830 is not set
+# CONFIG_DVB_RTL2832 is not set
+# CONFIG_DVB_RTL2832_SDR is not set
+# CONFIG_DVB_S5H1409 is not set
+# CONFIG_DVB_S5H1411 is not set
+# CONFIG_DVB_S5H1420 is not set
+# CONFIG_DVB_S5H1432 is not set
+# CONFIG_DVB_S921 is not set
+# CONFIG_DVB_SI2165 is not set
+# CONFIG_DVB_SI2168 is not set
+# CONFIG_DVB_SI21XX is not set
+# CONFIG_DVB_SP2 is not set
+# CONFIG_DVB_SP8870 is not set
+# CONFIG_DVB_SP887X is not set
+# CONFIG_DVB_STB0899 is not set
+# CONFIG_DVB_STB6000 is not set
+# CONFIG_DVB_STB6100 is not set
+# CONFIG_DVB_STV0288 is not set
+# CONFIG_DVB_STV0297 is not set
+# CONFIG_DVB_STV0299 is not set
+# CONFIG_DVB_STV0367 is not set
+# CONFIG_DVB_STV0900 is not set
+# CONFIG_DVB_STV090x is not set
+# CONFIG_DVB_STV0910 is not set
+# CONFIG_DVB_STV6110 is not set
+# CONFIG_DVB_STV6110x is not set
+# CONFIG_DVB_STV6111 is not set
+# CONFIG_DVB_TC90522 is not set
+# CONFIG_DVB_TDA10021 is not set
+# CONFIG_DVB_TDA10023 is not set
+# CONFIG_DVB_TDA10048 is not set
+# CONFIG_DVB_TDA1004X is not set
+# CONFIG_DVB_TDA10071 is not set
+# CONFIG_DVB_TDA10086 is not set
+# CONFIG_DVB_TDA18271C2DD is not set
+# CONFIG_DVB_TDA665x is not set
+# CONFIG_DVB_TDA8083 is not set
+# CONFIG_DVB_TDA8261 is not set
+# CONFIG_DVB_TDA826X is not set
+# CONFIG_DVB_TEST_DRIVERS is not set
+# CONFIG_DVB_TS2020 is not set
+# CONFIG_DVB_TTUSB_BUDGET is not set
+# CONFIG_DVB_TTUSB_DEC is not set
+# CONFIG_DVB_TUA6100 is not set
+# CONFIG_DVB_TUNER_CX24113 is not set
+# CONFIG_DVB_TUNER_DIB0070 is not set
+# CONFIG_DVB_TUNER_DIB0090 is not set
+# CONFIG_DVB_TUNER_ITD1000 is not set
+# CONFIG_DVB_ULE_DEBUG is not set
+# CONFIG_DVB_USB is not set
+# CONFIG_DVB_USB_V2 is not set
+# CONFIG_DVB_VES1820 is not set
+# CONFIG_DVB_VES1X93 is not set
+# CONFIG_DVB_ZD1301_DEMOD is not set
+# CONFIG_DVB_ZL10036 is not set
+# CONFIG_DVB_ZL10039 is not set
+# CONFIG_DVB_ZL10353 is not set
+# CONFIG_DWC_XLGMAC is not set
+# CONFIG_DWMAC_DWC_QOS_ETH is not set
+# CONFIG_DWMAC_INTEL_PLAT is not set
+# CONFIG_DWMAC_IPQ806X is not set
+# CONFIG_DWMAC_LOONGSON is not set
+# CONFIG_DWMAC_LPC18XX is not set
+# CONFIG_DWMAC_MESON is not set
+# CONFIG_DWMAC_ROCKCHIP is not set
+# CONFIG_DWMAC_SOCFPGA is not set
+# CONFIG_DWMAC_STI is not set
+# CONFIG_DW_AXI_DMAC is not set
+# CONFIG_DW_DMAC is not set
+# CONFIG_DW_DMAC_PCI is not set
+# CONFIG_DW_EDMA is not set
+# CONFIG_DW_EDMA_PCIE is not set
+# CONFIG_DW_WATCHDOG is not set
+# CONFIG_DW_XDATA_PCIE is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+# CONFIG_DYNAMIC_DEBUG_CORE is not set
+# CONFIG_E100 is not set
+# CONFIG_E1000 is not set
+# CONFIG_E1000E is not set
+# CONFIG_E1000E_HWTS is not set
+# CONFIG_EARLY_PRINTK_8250 is not set
+# CONFIG_EARLY_PRINTK_USB_XDBC is not set
+# CONFIG_EBC_C384_WDT is not set
+# CONFIG_ECHO is not set
+# CONFIG_ECRYPT_FS is not set
+# CONFIG_EDAC is not set
+# CONFIG_EEEPC_LAPTOP is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_EEPROM_93XX46 is not set
+# CONFIG_EEPROM_AT24 is not set
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_DIGSY_MTC_CFG is not set
+# CONFIG_EEPROM_EE1004 is not set
+# CONFIG_EEPROM_IDT_89HPESX is not set
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EFI is not set
+CONFIG_EFI_PARTITION=y
+# CONFIG_EFI_VARS_PSTORE is not set
+# CONFIG_EFS_FS is not set
+CONFIG_ELFCORE=y
+# CONFIG_ELF_CORE is not set
+# CONFIG_EMAC_ROCKCHIP is not set
+# CONFIG_EM_TIMER_STI is not set
+# CONFIG_ENA_ETHERNET is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_ENCRYPTED_KEYS is not set
+# CONFIG_ENCX24J600 is not set
+# CONFIG_ENERGY_MODEL is not set
+# CONFIG_ENIC is not set
+# CONFIG_ENVELOPE_DETECTOR is not set
+# CONFIG_EPAPR_PARAVIRT is not set
+# CONFIG_EPIC100 is not set
+CONFIG_EPOLL=y
+# CONFIG_EQUALIZER is not set
+# CONFIG_EROFS_FS is not set
+# CONFIG_ET131X is not set
+CONFIG_ETHERNET=y
+# CONFIG_ETHOC is not set
+CONFIG_ETHTOOL_NETLINK=y
+CONFIG_EVENTFD=y
+# CONFIG_EVM is not set
+# CONFIG_EXFAT_FS is not set
+CONFIG_EXPERT=y
+CONFIG_EXPORTFS=y
+# CONFIG_EXPORTFS_BLOCK_OPS is not set
+# CONFIG_EXT2_FS is not set
+CONFIG_EXT2_FS_XATTR=y
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_DEBUG is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_EXT4_FS_POSIX_ACL is not set
+# CONFIG_EXT4_FS_SECURITY is not set
+CONFIG_EXT4_USE_FOR_EXT2=y
+# CONFIG_EXTCON is not set
+# CONFIG_EXTCON_ADC_JACK is not set
+# CONFIG_EXTCON_AXP288 is not set
+# CONFIG_EXTCON_FSA9480 is not set
+# CONFIG_EXTCON_GPIO is not set
+# CONFIG_EXTCON_INTEL_INT3496 is not set
+# CONFIG_EXTCON_MAX3355 is not set
+# CONFIG_EXTCON_PTN5150 is not set
+# CONFIG_EXTCON_QCOM_SPMI_MISC is not set
+# CONFIG_EXTCON_RT8973A is not set
+# CONFIG_EXTCON_SM5502 is not set
+# CONFIG_EXTCON_USBC_TUSB320 is not set
+# CONFIG_EXTCON_USB_GPIO is not set
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_EXTRA_TARGETS=""
+# CONFIG_EXYNOS_ADC is not set
+# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_F2FS_CHECK_FS is not set
+# CONFIG_F2FS_FAULT_INJECTION is not set
+# CONFIG_F2FS_FS is not set
+# CONFIG_F2FS_FS_COMPRESSION is not set
+# CONFIG_F2FS_FS_POSIX_ACL is not set
+# CONFIG_F2FS_FS_SECURITY is not set
+CONFIG_F2FS_FS_XATTR=y
+# CONFIG_F2FS_IOSTAT is not set
+CONFIG_F2FS_STAT_FS=y
+# CONFIG_F2FS_UNFAIR_RWSEM is not set
+# CONFIG_FAILOVER is not set
+# CONFIG_FAIR_GROUP_SCHED is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_FAT_DEFAULT_UTF8 is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_FB is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_ARMCLCD is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_ATMEL is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_BIG_ENDIAN is not set
+# CONFIG_FB_BOTH_ENDIAN is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_CARMINE is not set
+# CONFIG_FB_CFB_COPYAREA is not set
+# CONFIG_FB_CFB_FILLRECT is not set
+# CONFIG_FB_CFB_IMAGEBLIT is not set
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_DA8XX is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_DEVICE is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_FSL_DIU is not set
+# CONFIG_FB_GEODE is not set
+# CONFIG_FB_GOLDFISH is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_I740 is not set
+# CONFIG_FB_IBM_GXT4500 is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_IMX is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_LE80578 is not set
+# CONFIG_FB_LITTLE_ENDIAN is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_N411 is not set
+# CONFIG_FB_NEOMAGIC is not set
+CONFIG_FB_NOTIFY=y
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_OF is not set
+# CONFIG_FB_OMAP2 is not set
+# CONFIG_FB_OPENCORES is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_PS3 is not set
+# CONFIG_FB_PXA is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIMPLE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_SM712 is not set
+# CONFIG_FB_SM750 is not set
+# CONFIG_FB_SMSCUFX is not set
+# CONFIG_FB_SSD1307 is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_TFT is not set
+# CONFIG_FB_TFT_AGM1264K_FL is not set
+# CONFIG_FB_TFT_BD663474 is not set
+# CONFIG_FB_TFT_HX8340BN is not set
+# CONFIG_FB_TFT_HX8347D is not set
+# CONFIG_FB_TFT_HX8353D is not set
+# CONFIG_FB_TFT_HX8357D is not set
+# CONFIG_FB_TFT_ILI9163 is not set
+# CONFIG_FB_TFT_ILI9320 is not set
+# CONFIG_FB_TFT_ILI9325 is not set
+# CONFIG_FB_TFT_ILI9340 is not set
+# CONFIG_FB_TFT_ILI9341 is not set
+# CONFIG_FB_TFT_ILI9481 is not set
+# CONFIG_FB_TFT_ILI9486 is not set
+# CONFIG_FB_TFT_PCD8544 is not set
+# CONFIG_FB_TFT_RA8875 is not set
+# CONFIG_FB_TFT_S6D02A1 is not set
+# CONFIG_FB_TFT_S6D1121 is not set
+# CONFIG_FB_TFT_SEPS525 is not set
+# CONFIG_FB_TFT_SH1106 is not set
+# CONFIG_FB_TFT_SSD1289 is not set
+# CONFIG_FB_TFT_SSD1305 is not set
+# CONFIG_FB_TFT_SSD1306 is not set
+# CONFIG_FB_TFT_SSD1331 is not set
+# CONFIG_FB_TFT_SSD1351 is not set
+# CONFIG_FB_TFT_ST7735R is not set
+# CONFIG_FB_TFT_ST7789V is not set
+# CONFIG_FB_TFT_TINYLCD is not set
+# CONFIG_FB_TFT_TLS8204 is not set
+# CONFIG_FB_TFT_UC1611 is not set
+# CONFIG_FB_TFT_UC1701 is not set
+# CONFIG_FB_TFT_UPD161704 is not set
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_UDL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_VGA16 is not set
+# CONFIG_FB_VIA is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FCOE is not set
+# CONFIG_FCOE_FNIC is not set
+# CONFIG_FDDI is not set
+# CONFIG_FEALNX is not set
+# CONFIG_FHANDLE is not set
+CONFIG_FIB_RULES=y
+# CONFIG_FIELDBUS_DEV is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_FIND_BIT_BENCHMARK is not set
+# CONFIG_FIREWIRE is not set
+# CONFIG_FIREWIRE_NOSY is not set
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FIRMWARE_MEMMAP is not set
+# CONFIG_FIXED_PHY is not set
+CONFIG_FLATMEM=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_FM10K is not set
+# CONFIG_FONTS is not set
+# CONFIG_FONT_6x8 is not set
+# CONFIG_FONT_TER16x32 is not set
+# CONFIG_FORCEDETH is not set
+# CONFIG_FORCE_NR_CPUS is not set
+CONFIG_FORTIFY_SOURCE=y
+# CONFIG_FPGA is not set
+# CONFIG_FPROBE is not set
+# CONFIG_FRAMEBUFFER_CONSOLE is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set
+# CONFIG_FRAME_POINTER is not set
+# CONFIG_FREEZER is not set
+# CONFIG_FSCACHE is not set
+# CONFIG_FSI is not set
+# CONFIG_FSL_DPAA2_SWITCH is not set
+# CONFIG_FSL_EDMA is not set
+# CONFIG_FSL_ENETC is not set
+# CONFIG_FSL_ENETC_IERB is not set
+# CONFIG_FSL_ENETC_MDIO is not set
+# CONFIG_FSL_ENETC_VF is not set
+# CONFIG_FSL_ERRATUM_A008585 is not set
+# CONFIG_FSL_MC_BUS is not set
+# CONFIG_FSL_PQ_MDIO is not set
+# CONFIG_FSL_QDMA is not set
+# CONFIG_FSL_RCPM is not set
+# CONFIG_FSL_XGMAC_MDIO is not set
+CONFIG_FSNOTIFY=y
+# CONFIG_FS_DAX is not set
+# CONFIG_FS_ENCRYPTION is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_FS_VERITY is not set
+# CONFIG_FTGMAC100 is not set
+# CONFIG_FTL is not set
+# CONFIG_FTMAC100 is not set
+# CONFIG_FTRACE is not set
+# CONFIG_FTRACE_RECORD_RECURSION is not set
+# CONFIG_FTRACE_SORT_STARTUP_TEST is not set
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_FTR_FIXUP_SELFTEST is not set
+# CONFIG_FTWDT010_WATCHDOG is not set
+# CONFIG_FUJITSU_ERRATUM_010001 is not set
+# CONFIG_FUJITSU_ES is not set
+# CONFIG_FUJITSU_LAPTOP is not set
+# CONFIG_FUJITSU_TABLET is not set
+# CONFIG_FUNCTION_ERROR_INJECTION is not set
+# CONFIG_FUNCTION_GRAPH_RETVAL is not set
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_FUN_ETH is not set
+# CONFIG_FUSE_FS is not set
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_FC is not set
+# CONFIG_FUSION_SAS is not set
+# CONFIG_FUSION_SPI is not set
+CONFIG_FUTEX=y
+CONFIG_FUTEX_PI=y
+# CONFIG_FW_CFG_SYSFS is not set
+# CONFIG_FW_DEVLINK_SYNC_STATE_TIMEOUT is not set
+CONFIG_FW_LOADER=y
+# CONFIG_FW_LOADER_COMPRESS is not set
+# CONFIG_FW_LOADER_DEBUG is not set
+CONFIG_FW_LOADER_USER_HELPER=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+# CONFIG_FW_UPLOAD is not set
+# CONFIG_FXAS21002C is not set
+# CONFIG_FXLS8962AF_I2C is not set
+# CONFIG_FXLS8962AF_SPI is not set
+# CONFIG_FXOS8700_I2C is not set
+# CONFIG_FXOS8700_SPI is not set
+CONFIG_GACT_PROB=y
+# CONFIG_GADGET_UAC1 is not set
+# CONFIG_GAMEPORT is not set
+# CONFIG_GCC_PLUGINS is not set
+# CONFIG_GCOV is not set
+# CONFIG_GCOV_KERNEL is not set
+# CONFIG_GDB_SCRIPTS is not set
+# CONFIG_GEMINI_ETHERNET is not set
+# CONFIG_GENERIC_ADC_BATTERY is not set
+# CONFIG_GENERIC_ADC_THERMAL is not set
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+# CONFIG_GENERIC_CPU_DEVICES is not set
+CONFIG_GENERIC_HWEIGHT=y
+# CONFIG_GENERIC_IRQ_DEBUGFS is not set
+CONFIG_GENERIC_IRQ_IPI=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_NET_UTILS=y
+# CONFIG_GENERIC_PHY is not set
+CONFIG_GENERIC_PTDUMP=y
+CONFIG_GENERIC_VDSO_TIME_NS=y
+# CONFIG_GENEVE is not set
+# CONFIG_GENWQE is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_GLOB_SELFTEST is not set
+# CONFIG_GNSS is not set
+# CONFIG_GOLDFISH is not set
+# CONFIG_GOOGLE_CBMEM is not set
+# CONFIG_GOOGLE_FIRMWARE is not set
+# CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT is not set
+# CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY is not set
+# CONFIG_GOOGLE_SMI is not set
+# CONFIG_GP2AP002 is not set
+# CONFIG_GP2AP020A00F is not set
+# CONFIG_GPD_POCKET_FAN is not set
+CONFIG_GPIOLIB=y
+CONFIG_GPIOLIB_FASTPATH_LIMIT=512
+# CONFIG_GPIO_104_DIO_48E is not set
+# CONFIG_GPIO_104_IDIO_16 is not set
+# CONFIG_GPIO_104_IDI_48 is not set
+# CONFIG_GPIO_74X164 is not set
+# CONFIG_GPIO_74XX_MMIO is not set
+# CONFIG_GPIO_ADNP is not set
+# CONFIG_GPIO_AGGREGATOR is not set
+# CONFIG_GPIO_ALTERA is not set
+# CONFIG_GPIO_AMD8111 is not set
+# CONFIG_GPIO_AMDPT is not set
+# CONFIG_GPIO_AMD_FCH is not set
+# CONFIG_GPIO_BCM_KONA is not set
+# CONFIG_GPIO_BRCMSTB is not set
+# CONFIG_GPIO_BT8XX is not set
+# CONFIG_GPIO_CADENCE is not set
+# CONFIG_GPIO_CASCADE is not set
+# CONFIG_GPIO_CDEV is not set
+# CONFIG_GPIO_CDEV_V1 is not set
+# CONFIG_GPIO_CS5535 is not set
+# CONFIG_GPIO_DS4520 is not set
+# CONFIG_GPIO_DWAPB is not set
+# CONFIG_GPIO_EM is not set
+# CONFIG_GPIO_EXAR is not set
+# CONFIG_GPIO_F7188X is not set
+# CONFIG_GPIO_FTGPIO010 is not set
+# CONFIG_GPIO_FXL6408 is not set
+# CONFIG_GPIO_GENERIC_PLATFORM is not set
+# CONFIG_GPIO_GPIO_MM is not set
+# CONFIG_GPIO_GRGPIO is not set
+# CONFIG_GPIO_GW_PLD is not set
+# CONFIG_GPIO_HISI is not set
+# CONFIG_GPIO_HLWD is not set
+# CONFIG_GPIO_ICH is not set
+# CONFIG_GPIO_IT87 is not set
+# CONFIG_GPIO_LATCH is not set
+# CONFIG_GPIO_LOGICVC is not set
+# CONFIG_GPIO_MAX3191X is not set
+# CONFIG_GPIO_MAX7300 is not set
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_MB86S7X is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_ML_IOH is not set
+# CONFIG_GPIO_MOCKUP is not set
+# CONFIG_GPIO_MPC8XXX is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCA953X_IRQ is not set
+# CONFIG_GPIO_PCA9570 is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_GPIO_PCH is not set
+# CONFIG_GPIO_PCIE_IDIO_24 is not set
+# CONFIG_GPIO_PCI_IDIO_16 is not set
+# CONFIG_GPIO_PISOSR is not set
+# CONFIG_GPIO_PL061 is not set
+# CONFIG_GPIO_RCAR is not set
+# CONFIG_GPIO_RDC321X is not set
+# CONFIG_GPIO_SAMA5D2_PIOBU is not set
+# CONFIG_GPIO_SCH is not set
+# CONFIG_GPIO_SCH311X is not set
+# CONFIG_GPIO_SIFIVE is not set
+# CONFIG_GPIO_SIM is not set
+# CONFIG_GPIO_SYSCON is not set
+CONFIG_GPIO_SYSFS=y
+# CONFIG_GPIO_TPIC2810 is not set
+# CONFIG_GPIO_TS4900 is not set
+# CONFIG_GPIO_TS5500 is not set
+# CONFIG_GPIO_VIRTIO is not set
+# CONFIG_GPIO_VX855 is not set
+# CONFIG_GPIO_WATCHDOG is not set
+# CONFIG_GPIO_WINBOND is not set
+# CONFIG_GPIO_WS16C48 is not set
+# CONFIG_GPIO_XGENE is not set
+# CONFIG_GPIO_XILINX is not set
+# CONFIG_GPIO_XRA1403 is not set
+# CONFIG_GPIO_ZEVIO is not set
+# CONFIG_GP_PCI1XXXX is not set
+# CONFIG_GREENASIA_FF is not set
+# CONFIG_GREYBUS is not set
+# CONFIG_GTP is not set
+# CONFIG_GUP_TEST is not set
+# CONFIG_GVE is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_HAPPYMEAL is not set
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_HARDEN_BRANCH_HISTORY=y
+# CONFIG_HARDLOCKUP_DETECTOR is not set
+# CONFIG_HAVE_ARM_ARCH_TIMER is not set
+# CONFIG_HCALL_STATS is not set
+# CONFIG_HDC100X is not set
+# CONFIG_HDC2010 is not set
+# CONFIG_HDLC is not set
+# CONFIG_HDLC_CISCO is not set
+# CONFIG_HDLC_FR is not set
+# CONFIG_HDLC_PPP is not set
+# CONFIG_HDLC_RAW is not set
+# CONFIG_HDLC_RAW_ETH is not set
+# CONFIG_HDMI_LPE_AUDIO is not set
+# CONFIG_HDQ_MASTER_OMAP is not set
+# CONFIG_HEADERS_INSTALL is not set
+# CONFIG_HERMES is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HI6421V600_IRQ is not set
+# CONFIG_HI8435 is not set
+# CONFIG_HIBERNATION is not set
+# CONFIG_HID is not set
+# CONFIG_HIDRAW is not set
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACCUTOUCH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_ACRUX_FF is not set
+# CONFIG_HID_ALPS is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_APPLEIR is not set
+# CONFIG_HID_ASUS is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BATTERY_STRENGTH is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_BETOP_FF is not set
+# CONFIG_HID_BIGBEN_FF is not set
+# CONFIG_HID_BPF is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_CMEDIA is not set
+# CONFIG_HID_CORSAIR is not set
+# CONFIG_HID_COUGAR is not set
+# CONFIG_HID_CP2112 is not set
+# CONFIG_HID_CREATIVE_SB0540 is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_ELAN is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_ELO is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_EVISION is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_FT260 is not set
+# CONFIG_HID_GEMBIRD is not set
+# CONFIG_HID_GENERIC is not set
+# CONFIG_HID_GFRM is not set
+# CONFIG_HID_GLORIOUS is not set
+# CONFIG_HID_GOOGLE_HAMMER is not set
+# CONFIG_HID_GOOGLE_STADIA_FF is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_GT683R is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_HOLTEK is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_ITE is not set
+# CONFIG_HID_JABRA is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LED is not set
+# CONFIG_HID_LENOVO is not set
+# CONFIG_HID_LETSKETCH is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_LOGITECH_DJ is not set
+# CONFIG_HID_LOGITECH_HIDPP is not set
+# CONFIG_HID_MACALLY is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MALTRON is not set
+# CONFIG_HID_MAYFLASH is not set
+# CONFIG_HID_MCP2200 is not set
+# CONFIG_HID_MCP2221 is not set
+# CONFIG_HID_MEGAWORLD_FF is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_NINTENDO is not set
+# CONFIG_HID_NTI is not set
+# CONFIG_HID_NTRIG is not set
+# CONFIG_HID_NVIDIA_SHIELD is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PENMOUNT is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PID is not set
+# CONFIG_HID_PLANTRONICS is not set
+# CONFIG_HID_PLAYSTATION is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_PRODIKEYS is not set
+# CONFIG_HID_PXRC is not set
+# CONFIG_HID_RAZER is not set
+# CONFIG_HID_REDRAGON is not set
+# CONFIG_HID_RETRODE is not set
+# CONFIG_HID_RMI is not set
+# CONFIG_HID_ROCCAT is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SEMITEK is not set
+# CONFIG_HID_SENSOR_HUB is not set
+# CONFIG_HID_SIGMAMICRO is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_SONY is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEAM is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_HID_THINGM is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPRE is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_U2FZERO is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_UDRAW_PS3 is not set
+# CONFIG_HID_VIEWSONIC is not set
+# CONFIG_HID_VIVALDI is not set
+# CONFIG_HID_VRC2 is not set
+# CONFIG_HID_WACOM is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_WIIMOTE is not set
+# CONFIG_HID_XIAOMI is not set
+# CONFIG_HID_XINMO is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_HIGH_RES_TIMERS=y
+# CONFIG_HINIC is not set
+# CONFIG_HIP04_ETH is not set
+# CONFIG_HIPPI is not set
+# CONFIG_HISILICON_ERRATUM_161010101 is not set
+# CONFIG_HISILICON_ERRATUM_161600802 is not set
+# CONFIG_HISI_DMA is not set
+# CONFIG_HISI_FEMAC is not set
+# CONFIG_HISI_HIKEY_USB is not set
+# CONFIG_HISI_PCIE_PMU is not set
+# CONFIG_HISI_PTT is not set
+# CONFIG_HIST_TRIGGERS_DEBUG is not set
+# CONFIG_HIX5HD2_GMAC is not set
+# CONFIG_HMC425 is not set
+# CONFIG_HMC6352 is not set
+# CONFIG_HNS is not set
+# CONFIG_HNS3 is not set
+# CONFIG_HNS3_PMU is not set
+# CONFIG_HNS_DSAF is not set
+# CONFIG_HNS_ENET is not set
+# CONFIG_HOSTAP is not set
+# CONFIG_HOSTAP_CS is not set
+# CONFIG_HOSTAP_PCI is not set
+# CONFIG_HOSTAP_PLX is not set
+# CONFIG_HOTPLUG_CPU is not set
+# CONFIG_HOTPLUG_PCI is not set
+# CONFIG_HP03 is not set
+# CONFIG_HP206C is not set
+CONFIG_HPET_MMAP_DEFAULT=y
+# CONFIG_HPFS_FS is not set
+# CONFIG_HP_ILO is not set
+# CONFIG_HP_WATCHDOG is not set
+# CONFIG_HSA_AMD is not set
+# CONFIG_HSI is not set
+# CONFIG_HSR is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTE is not set
+# CONFIG_HTS221 is not set
+# CONFIG_HTU21 is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_HVC_UDBG is not set
+# CONFIG_HWLAT_TRACER is not set
+# CONFIG_HWMON is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+# CONFIG_HWMON_VID is not set
+# CONFIG_HWSPINLOCK is not set
+# CONFIG_HWSPINLOCK_OMAP is not set
+CONFIG_HW_PERF_EVENTS=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HW_RANDOM_AMD is not set
+# CONFIG_HW_RANDOM_ARM_SMCCC_TRNG is not set
+# CONFIG_HW_RANDOM_ATMEL is not set
+# CONFIG_HW_RANDOM_BA431 is not set
+# CONFIG_HW_RANDOM_BCM2835 is not set
+# CONFIG_HW_RANDOM_CAVIUM is not set
+# CONFIG_HW_RANDOM_CCTRNG is not set
+# CONFIG_HW_RANDOM_CN10K is not set
+# CONFIG_HW_RANDOM_EXYNOS is not set
+# CONFIG_HW_RANDOM_GEODE is not set
+# CONFIG_HW_RANDOM_INTEL is not set
+# CONFIG_HW_RANDOM_IPROC_RNG200 is not set
+# CONFIG_HW_RANDOM_MTK is not set
+# CONFIG_HW_RANDOM_OMAP is not set
+# CONFIG_HW_RANDOM_OMAP3_ROM is not set
+# CONFIG_HW_RANDOM_PPC4XX is not set
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+CONFIG_HW_RANDOM_TPM=y
+# CONFIG_HW_RANDOM_VIA is not set
+# CONFIG_HW_RANDOM_VIRTIO is not set
+# CONFIG_HW_RANDOM_XIPHERA is not set
+# CONFIG_HX711 is not set
+# CONFIG_HYPERV is not set
+CONFIG_HZ=100
+CONFIG_HZ_100=y
+# CONFIG_HZ_1000 is not set
+# CONFIG_HZ_1024 is not set
+# CONFIG_HZ_128 is not set
+# CONFIG_HZ_200 is not set
+# CONFIG_HZ_24 is not set
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_256 is not set
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_48 is not set
+# CONFIG_HZ_500 is not set
+# CONFIG_HZ_PERIODIC is not set
+# CONFIG_I2C is not set
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCA is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set
+# CONFIG_I2C_AU1550 is not set
+# CONFIG_I2C_BCM2835 is not set
+# CONFIG_I2C_BCM_IPROC is not set
+# CONFIG_I2C_BRCMSTB is not set
+# CONFIG_I2C_CADENCE is not set
+# CONFIG_I2C_CBUS_GPIO is not set
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_CP2615 is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEMUX_PINCTRL is not set
+# CONFIG_I2C_DESIGNWARE_PCI is not set
+# CONFIG_I2C_DESIGNWARE_PLATFORM is not set
+# CONFIG_I2C_DESIGNWARE_SLAVE is not set
+# CONFIG_I2C_DIOLAN_U2C is not set
+# CONFIG_I2C_EG20T is not set
+# CONFIG_I2C_ELEKTOR is not set
+# CONFIG_I2C_EMEV2 is not set
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+# CONFIG_I2C_HID is not set
+# CONFIG_I2C_HID_OF is not set
+# CONFIG_I2C_HID_OF_ELAN is not set
+# CONFIG_I2C_HID_OF_GOODIX is not set
+# CONFIG_I2C_HISI is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_IBM_IIC is not set
+# CONFIG_I2C_IMG is not set
+# CONFIG_I2C_ISCH is not set
+# CONFIG_I2C_ISMT is not set
+# CONFIG_I2C_JZ4780 is not set
+# CONFIG_I2C_MLXCPLD is not set
+# CONFIG_I2C_MPC is not set
+# CONFIG_I2C_MT65XX is not set
+# CONFIG_I2C_MUX is not set
+# CONFIG_I2C_MUX_GPIO is not set
+# CONFIG_I2C_MUX_GPMUX is not set
+# CONFIG_I2C_MUX_LTC4306 is not set
+# CONFIG_I2C_MUX_MLXCPLD is not set
+# CONFIG_I2C_MUX_PCA9541 is not set
+# CONFIG_I2C_MUX_PCA954x is not set
+# CONFIG_I2C_MUX_PINCTRL is not set
+# CONFIG_I2C_MUX_REG is not set
+# CONFIG_I2C_MV64XXX is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_NOMADIK is not set
+# CONFIG_I2C_NVIDIA_GPU is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_OCTEON is not set
+# CONFIG_I2C_PARPORT is not set
+# CONFIG_I2C_PCA_ISA is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_PCI1XXXX is not set
+# CONFIG_I2C_PIIX4 is not set
+# CONFIG_I2C_PXA_PCI is not set
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_RCAR is not set
+# CONFIG_I2C_RK3X is not set
+# CONFIG_I2C_ROBOTFUZZ_OSIF is not set
+# CONFIG_I2C_S3C2410 is not set
+# CONFIG_I2C_SCMI is not set
+# CONFIG_I2C_SH_MOBILE is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_SLAVE is not set
+# CONFIG_I2C_SLAVE_EEPROM is not set
+# CONFIG_I2C_SMBUS is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_THUNDERX is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_VERSATILE is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_VIRTIO is not set
+# CONFIG_I2C_XILINX is not set
+# CONFIG_I3C is not set
+# CONFIG_I40E is not set
+# CONFIG_I40EVF is not set
+# CONFIG_I40E_DCB is not set
+# CONFIG_I6300ESB_WDT is not set
+# CONFIG_I82092 is not set
+# CONFIG_I82365 is not set
+# CONFIG_IAQCORE is not set
+# CONFIG_IBM_ASM is not set
+# CONFIG_IBM_EMAC_DEBUG is not set
+# CONFIG_IBM_EMAC_EMAC4 is not set
+# CONFIG_IBM_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_IBM_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_EMAC_RGMII is not set
+# CONFIG_IBM_EMAC_TAH is not set
+# CONFIG_IBM_EMAC_ZMII is not set
+# CONFIG_ICE is not set
+# CONFIG_ICP10100 is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_IDEAPAD_LAPTOP is not set
+# CONFIG_IDLE_PAGE_TRACKING is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_IEEE802154_ADF7242 is not set
+# CONFIG_IEEE802154_ATUSB is not set
+# CONFIG_IEEE802154_CA8210 is not set
+# CONFIG_IEEE802154_HWSIM is not set
+# CONFIG_IEEE802154_MCR20A is not set
+# CONFIG_IFB is not set
+# CONFIG_IGB is not set
+# CONFIG_IGBVF is not set
+# CONFIG_IGC is not set
+# CONFIG_IIO is not set
+# CONFIG_IIO_BUFFER is not set
+# CONFIG_IIO_BUFFER_CB is not set
+# CONFIG_IIO_BUFFER_DMA is not set
+# CONFIG_IIO_BUFFER_DMAENGINE is not set
+# CONFIG_IIO_BUFFER_HW_CONSUMER is not set
+# CONFIG_IIO_CONFIGFS is not set
+CONFIG_IIO_CONSUMERS_PER_TRIGGER=2
+# CONFIG_IIO_CROS_EC_ACCEL_LEGACY is not set
+# CONFIG_IIO_INTERRUPT_TRIGGER is not set
+# CONFIG_IIO_KX022A_I2C is not set
+# CONFIG_IIO_KX022A_SPI is not set
+# CONFIG_IIO_MUX is not set
+# CONFIG_IIO_RESCALE is not set
+# CONFIG_IIO_SIMPLE_DUMMY is not set
+# CONFIG_IIO_SSP_SENSORHUB is not set
+# CONFIG_IIO_ST_ACCEL_3AXIS is not set
+# CONFIG_IIO_ST_GYRO_3AXIS is not set
+# CONFIG_IIO_ST_LSM6DSX is not set
+# CONFIG_IIO_ST_LSM9DS0 is not set
+# CONFIG_IIO_ST_MAGN_3AXIS is not set
+# CONFIG_IIO_ST_PRESS is not set
+# CONFIG_IIO_SW_DEVICE is not set
+# CONFIG_IIO_SW_TRIGGER is not set
+# CONFIG_IIO_SYSFS_TRIGGER is not set
+# CONFIG_IIO_TRIGGER is not set
+# CONFIG_IIO_TRIGGERED_EVENT is not set
+# CONFIG_IKCONFIG is not set
+# CONFIG_IKCONFIG_PROC is not set
+# CONFIG_IKHEADERS is not set
+# CONFIG_IMA is not set
+# CONFIG_IMGPDC_WDT is not set
+# CONFIG_IMG_MDC_DMA is not set
+# CONFIG_IMX7D_ADC is not set
+# CONFIG_IMX8QXP_ADC is not set
+# CONFIG_IMX93_ADC is not set
+# CONFIG_IMX_IPUV3_CORE is not set
+# CONFIG_IMX_THERMAL is not set
+# CONFIG_INA2XX_ADC is not set
+# CONFIG_INDIRECT_PIO is not set
+CONFIG_INET=y
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_ESPINTCP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_ESPINTCP is not set
+# CONFIG_INET_IPCOMP is not set
+CONFIG_INET_TABLE_PERTURB_ORDER=16
+# CONFIG_INET_TCP_DIAG is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_UDP_DIAG is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INFINIBAND is not set
+# CONFIG_INFTL is not set
+# CONFIG_INGENIC_ADC is not set
+# CONFIG_INGENIC_CGU_JZ4725B is not set
+# CONFIG_INGENIC_CGU_JZ4740 is not set
+# CONFIG_INGENIC_CGU_JZ4755 is not set
+# CONFIG_INGENIC_CGU_JZ4760 is not set
+# CONFIG_INGENIC_CGU_JZ4770 is not set
+# CONFIG_INGENIC_CGU_JZ4780 is not set
+# CONFIG_INGENIC_CGU_X1000 is not set
+# CONFIG_INGENIC_CGU_X1830 is not set
+# CONFIG_INGENIC_OST is not set
+# CONFIG_INGENIC_SYSOST is not set
+# CONFIG_INGENIC_TCU_CLK is not set
+# CONFIG_INGENIC_TCU_IRQ is not set
+# CONFIG_INGENIC_TIMER is not set
+# CONFIG_INITRAMFS_PRESERVE_MTIME is not set
+CONFIG_INIT_ENV_ARG_LIMIT=32
+# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set
+# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set
+# CONFIG_INIT_STACK_ALL_PATTERN is not set
+# CONFIG_INIT_STACK_ALL_ZERO is not set
+CONFIG_INIT_STACK_NONE=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_INPUT is not set
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_ADXL34X is not set
+# CONFIG_INPUT_APANEL is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_ATLAS_BTNS is not set
+# CONFIG_INPUT_ATMEL_CAPTOUCH is not set
+# CONFIG_INPUT_AXP20X_PEK is not set
+# CONFIG_INPUT_BMA150 is not set
+# CONFIG_INPUT_CM109 is not set
+# CONFIG_INPUT_CMA3000 is not set
+# CONFIG_INPUT_DA7280_HAPTICS is not set
+# CONFIG_INPUT_DRV260X_HAPTICS is not set
+# CONFIG_INPUT_DRV2665_HAPTICS is not set
+# CONFIG_INPUT_DRV2667_HAPTICS is not set
+# CONFIG_INPUT_E3X0_BUTTON is not set
+# CONFIG_INPUT_EVBUG is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_GPIO_BEEPER is not set
+# CONFIG_INPUT_GPIO_DECODER is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+# CONFIG_INPUT_GPIO_VIBRA is not set
+# CONFIG_INPUT_IBM_PANEL is not set
+# CONFIG_INPUT_IDEAPAD_SLIDEBAR is not set
+# CONFIG_INPUT_IMS_PCU is not set
+# CONFIG_INPUT_IQS269A is not set
+# CONFIG_INPUT_IQS626A is not set
+# CONFIG_INPUT_IQS7222 is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_KXTJ9 is not set
+# CONFIG_INPUT_LEDS is not set
+# CONFIG_INPUT_MATRIXKMAP is not set
+# CONFIG_INPUT_MAX8997_HAPTIC is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_MMA8450 is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_PALMAS_PWRBUTTON is not set
+# CONFIG_INPUT_PCF8574 is not set
+# CONFIG_INPUT_PCSPKR is not set
+# CONFIG_INPUT_PM8941_PWRKEY is not set
+# CONFIG_INPUT_PM8XXX_VIBRATOR is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_PWM_BEEPER is not set
+# CONFIG_INPUT_PWM_VIBRA is not set
+# CONFIG_INPUT_REGULATOR_HAPTIC is not set
+# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_TPS65218_PWRBUTTON is not set
+# CONFIG_INPUT_TWL4030_PWRBUTTON is not set
+# CONFIG_INPUT_TWL4030_VIBRA is not set
+# CONFIG_INPUT_TWL6040_VIBRA is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_INPUT_WISTRON_BTNS is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INT340X_THERMAL is not set
+# CONFIG_INTEGRITY is not set
+# CONFIG_INTEGRITY_AUDIT is not set
+# CONFIG_INTEGRITY_SIGNATURE is not set
+# CONFIG_INTEL_ATOMISP2_LED is not set
+# CONFIG_INTEL_ATOMISP2_PM is not set
+# CONFIG_INTEL_HID_EVENT is not set
+# CONFIG_INTEL_IDLE is not set
+# CONFIG_INTEL_IDMA64 is not set
+# CONFIG_INTEL_INT0002_VGPIO is not set
+# CONFIG_INTEL_IOATDMA is not set
+# CONFIG_INTEL_ISH_HID is not set
+# CONFIG_INTEL_MEI is not set
+# CONFIG_INTEL_MEI_GSC_PROXY is not set
+# CONFIG_INTEL_MEI_HDCP is not set
+# CONFIG_INTEL_MEI_ME is not set
+# CONFIG_INTEL_MEI_PXP is not set
+# CONFIG_INTEL_MEI_TXE is not set
+# CONFIG_INTEL_OAKTRAIL is not set
+# CONFIG_INTEL_PMC_CORE is not set
+# CONFIG_INTEL_PUNIT_IPC is not set
+# CONFIG_INTEL_RST is not set
+# CONFIG_INTEL_SMARTCONNECT is not set
+# CONFIG_INTEL_SOC_PMIC is not set
+# CONFIG_INTEL_SOC_PMIC_CHTDC_TI is not set
+# CONFIG_INTEL_SOC_PMIC_CHTWC is not set
+# CONFIG_INTEL_TH is not set
+# CONFIG_INTEL_VBTN is not set
+# CONFIG_INTEL_XWAY_PHY is not set
+# CONFIG_INTERCONNECT is not set
+# CONFIG_INTERVAL_TREE_TEST is not set
+# CONFIG_INV_ICM42600_I2C is not set
+# CONFIG_INV_ICM42600_SPI is not set
+# CONFIG_INV_MPU6050_I2C is not set
+# CONFIG_INV_MPU6050_IIO is not set
+# CONFIG_INV_MPU6050_SPI is not set
+# CONFIG_IOMMU_SUPPORT is not set
+# CONFIG_IONIC is not set
+# CONFIG_IOSCHED_BFQ is not set
+# CONFIG_IOSM is not set
+CONFIG_IO_STRICT_DEVMEM=y
+# CONFIG_IO_URING is not set
+CONFIG_IO_WQ=y
+# CONFIG_IP17XX_PHY is not set
+# CONFIG_IP5XXX_POWER is not set
+# CONFIG_IP6_NF_FILTER is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP6_NF_MANGLE is not set
+# CONFIG_IP6_NF_MATCH_AH is not set
+# CONFIG_IP6_NF_MATCH_EUI64 is not set
+# CONFIG_IP6_NF_MATCH_FRAG is not set
+# CONFIG_IP6_NF_MATCH_HL is not set
+# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set
+# CONFIG_IP6_NF_MATCH_MH is not set
+# CONFIG_IP6_NF_MATCH_OPTS is not set
+# CONFIG_IP6_NF_MATCH_RPFILTER is not set
+# CONFIG_IP6_NF_MATCH_RT is not set
+# CONFIG_IP6_NF_MATCH_SRH is not set
+# CONFIG_IP6_NF_NAT is not set
+# CONFIG_IP6_NF_RAW is not set
+# CONFIG_IP6_NF_SECURITY is not set
+# CONFIG_IP6_NF_TARGET_HL is not set
+# CONFIG_IP6_NF_TARGET_MASQUERADE is not set
+# CONFIG_IP6_NF_TARGET_REJECT is not set
+# CONFIG_IP6_NF_TARGET_SYNPROXY is not set
+# CONFIG_IPACK_BUS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_IPMB_DEVICE_INTERFACE is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_IPU_BRIDGE is not set
+# CONFIG_IPV6 is not set
+# CONFIG_IPV6_FOU is not set
+# CONFIG_IPV6_FOU_TUNNEL is not set
+# CONFIG_IPV6_ILA is not set
+# CONFIG_IPV6_IOAM6_LWTUNNEL is not set
+# CONFIG_IPV6_MIP6 is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_IPV6_MROUTE_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+CONFIG_IPV6_NDISC_NODETYPE=y
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_ROUTE_INFO is not set
+# CONFIG_IPV6_RPL_LWTUNNEL is not set
+# CONFIG_IPV6_SEG6_HMAC is not set
+# CONFIG_IPV6_SEG6_LWTUNNEL is not set
+# CONFIG_IPV6_SIT is not set
+# CONFIG_IPV6_SIT_6RD is not set
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_IPV6_VTI is not set
+# CONFIG_IPVLAN is not set
+# CONFIG_IPVTAP is not set
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2100_DEBUG is not set
+CONFIG_IPW2100_MONITOR=y
+# CONFIG_IPW2200 is not set
+# CONFIG_IPW2200_DEBUG is not set
+CONFIG_IPW2200_MONITOR=y
+# CONFIG_IPW2200_PROMISCUOUS is not set
+# CONFIG_IPW2200_QOS is not set
+# CONFIG_IPW2200_RADIOTAP is not set
+# CONFIG_IPWIRELESS is not set
+CONFIG_IP_ADVANCED_ROUTER=y
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_FIB_TRIE_STATS is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_MULTIPLE_TABLES=y
+# CONFIG_IP_NF_ARPFILTER is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+# CONFIG_IP_NF_ARP_MANGLE is not set
+# CONFIG_IP_NF_FILTER is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_MANGLE is not set
+# CONFIG_IP_NF_MATCH_AH is not set
+# CONFIG_IP_NF_MATCH_ECN is not set
+# CONFIG_IP_NF_MATCH_RPFILTER is not set
+# CONFIG_IP_NF_MATCH_TTL is not set
+# CONFIG_IP_NF_RAW is not set
+# CONFIG_IP_NF_SECURITY is not set
+# CONFIG_IP_NF_TARGET_ECN is not set
+# CONFIG_IP_NF_TARGET_MASQUERADE is not set
+# CONFIG_IP_NF_TARGET_NETMAP is not set
+# CONFIG_IP_NF_TARGET_REDIRECT is not set
+# CONFIG_IP_NF_TARGET_REJECT is not set
+# CONFIG_IP_NF_TARGET_SYNPROXY is not set
+# CONFIG_IP_NF_TARGET_TTL is not set
+# CONFIG_IP_PIMSM_V1 is not set
+# CONFIG_IP_PIMSM_V2 is not set
+# CONFIG_IP_PNP is not set
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+# CONFIG_IP_SCTP is not set
+# CONFIG_IP_SET is not set
+# CONFIG_IP_SET_HASH_IPMAC is not set
+# CONFIG_IP_VS is not set
+# CONFIG_IP_VS_MH is not set
+CONFIG_IP_VS_MH_TAB_INDEX=10
+# CONFIG_IP_VS_TWOS is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_IRQ_ALL_CPUS is not set
+# CONFIG_IRQ_POLL is not set
+# CONFIG_IRQ_TIME_ACCOUNTING is not set
+# CONFIG_IRSD200 is not set
+# CONFIG_IR_GPIO_CIR is not set
+# CONFIG_IR_HIX5HD2 is not set
+# CONFIG_IR_IGORPLUGUSB is not set
+# CONFIG_IR_IGUANA is not set
+# CONFIG_IR_IMG is not set
+# CONFIG_IR_IMON is not set
+# CONFIG_IR_IMON_RAW is not set
+# CONFIG_IR_JVC_DECODER is not set
+# CONFIG_IR_MCEUSB is not set
+# CONFIG_IR_NEC_DECODER is not set
+# CONFIG_IR_RC5_DECODER is not set
+# CONFIG_IR_RC6_DECODER is not set
+# CONFIG_IR_REDRAT3 is not set
+# CONFIG_IR_SERIAL is not set
+# CONFIG_IR_SONY_DECODER is not set
+# CONFIG_IR_STREAMZAP is not set
+# CONFIG_IR_TOY is not set
+# CONFIG_IR_TTUSBIR is not set
+# CONFIG_ISA_BUS is not set
+# CONFIG_ISA_BUS_API is not set
+# CONFIG_ISCSI_BOOT_SYSFS is not set
+# CONFIG_ISCSI_TCP is not set
+CONFIG_ISDN=y
+# CONFIG_ISDN_CAPI is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_ISL29020 is not set
+# CONFIG_ISL29125 is not set
+# CONFIG_ISL29501 is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_ISS4xx is not set
+# CONFIG_ITG3200 is not set
+# CONFIG_IWL3945 is not set
+# CONFIG_IWLWIFI is not set
+# CONFIG_IXGBE is not set
+# CONFIG_IXGBEVF is not set
+# CONFIG_IXGBE_DCB is not set
+# CONFIG_JAILHOUSE_GUEST is not set
+# CONFIG_JBD2_DEBUG is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS_POSIX_ACL is not set
+# CONFIG_JFFS2_FS_SECURITY is not set
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+CONFIG_JFFS2_FS_XATTR=y
+CONFIG_JFFS2_LZMA=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_ZLIB is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_SECURITY is not set
+# CONFIG_JFS_STATISTICS is not set
+# CONFIG_JME is not set
+CONFIG_JOLIET=y
+# CONFIG_JSA1212 is not set
+# CONFIG_JUMP_LABEL is not set
+# CONFIG_JZ4740_WDT is not set
+# CONFIG_KALLSYMS is not set
+# CONFIG_KALLSYMS_ABSOLUTE_PERCPU is not set
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_KALLSYMS_BASE_RELATIVE=y
+# CONFIG_KALLSYMS_SELFTEST is not set
+# CONFIG_KALLSYMS_UNCOMPRESSED is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_KASAN is not set
+# CONFIG_KASAN_MODULE_TEST is not set
+CONFIG_KASAN_STACK=y
+# CONFIG_KCMP is not set
+# CONFIG_KCOV is not set
+CONFIG_KCOV_IRQ_AREA_SIZE=0x40000
+# CONFIG_KCSAN is not set
+# CONFIG_KERNEL_BZIP2 is not set
+# CONFIG_KERNEL_GZIP is not set
+# CONFIG_KERNEL_LZ4 is not set
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_KERNEL_MODE_NEON=y
+CONFIG_KERNEL_XZ=y
+# CONFIG_KERNEL_ZSTD is not set
+CONFIG_KERNFS=y
+# CONFIG_KEXEC is not set
+# CONFIG_KEXEC_FILE is not set
+# CONFIG_KEXEC_SIG is not set
+# CONFIG_KEYBOARD_ADC is not set
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+# CONFIG_KEYBOARD_APPLESPI is not set
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_BCM is not set
+# CONFIG_KEYBOARD_CAP11XX is not set
+# CONFIG_KEYBOARD_CYPRESS_SF is not set
+# CONFIG_KEYBOARD_DLINK_DIR685 is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_KEYBOARD_GPIO_POLLED is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+# CONFIG_KEYBOARD_LM8333 is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_MT6779 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OMAP4 is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_PINEPHONE is not set
+# CONFIG_KEYBOARD_PXA27x is not set
+# CONFIG_KEYBOARD_QT1050 is not set
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_SAMSUNG is not set
+# CONFIG_KEYBOARD_SH_KEYSC is not set
+# CONFIG_KEYBOARD_SNVS_PWRKEY is not set
+# CONFIG_KEYBOARD_STMPE is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_TCA8418 is not set
+# CONFIG_KEYBOARD_TEGRA is not set
+# CONFIG_KEYBOARD_TM2_TOUCHKEY is not set
+# CONFIG_KEYBOARD_TWL4030 is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYS is not set
+# CONFIG_KEYS_REQUEST_CACHE is not set
+# CONFIG_KEY_DH_OPERATIONS is not set
+# CONFIG_KFENCE is not set
+# CONFIG_KGDB is not set
+# CONFIG_KMX61 is not set
+# CONFIG_KPROBES is not set
+# CONFIG_KPROBES_SANITY_TEST is not set
+# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set
+# CONFIG_KPROBE_EVENT_GEN_TEST is not set
+# CONFIG_KS7010 is not set
+# CONFIG_KS8842 is not set
+# CONFIG_KS8851 is not set
+# CONFIG_KS8851_MLL is not set
+# CONFIG_KSM is not set
+# CONFIG_KSZ884X_PCI is not set
+# CONFIG_KUNIT is not set
+CONFIG_KUSER_HELPERS=y
+# CONFIG_KVM_AMD is not set
+# CONFIG_KVM_AMD_SEV is not set
+# CONFIG_KVM_GUEST is not set
+# CONFIG_KVM_INTEL is not set
+# CONFIG_KVM_WERROR is not set
+# CONFIG_KVM_XEN is not set
+# CONFIG_KXCJK1013 is not set
+# CONFIG_KXSD9 is not set
+# CONFIG_L2TP is not set
+# CONFIG_L2TP_ETH is not set
+# CONFIG_L2TP_IP is not set
+# CONFIG_L2TP_V3 is not set
+# CONFIG_LAN743X is not set
+# CONFIG_LAN966X_SWITCH is not set
+# CONFIG_LANTIQ is not set
+# CONFIG_LAPB is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+# CONFIG_LCD_AMS369FG06 is not set
+# CONFIG_LCD_CLASS_DEVICE is not set
+# CONFIG_LCD_HX8357 is not set
+# CONFIG_LCD_ILI922X is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_L4F00242T03 is not set
+# CONFIG_LCD_LMS283GF05 is not set
+# CONFIG_LCD_LMS501KF03 is not set
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_OTM3225A is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+CONFIG_LDISC_AUTOLOAD=y
+# CONFIG_LDM_PARTITION is not set
+CONFIG_LD_DEAD_CODE_DATA_ELIMINATION=y
+# CONFIG_LD_HEAD_STUB_CATCH is not set
+# CONFIG_LEDS_AN30259A is not set
+# CONFIG_LEDS_APU is not set
+# CONFIG_LEDS_AW200XX is not set
+# CONFIG_LEDS_AW2013 is not set
+# CONFIG_LEDS_BCM6328 is not set
+# CONFIG_LEDS_BCM6358 is not set
+# CONFIG_LEDS_BD2606MVV is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_BLINKM is not set
+CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y
+CONFIG_LEDS_CLASS=y
+# CONFIG_LEDS_CLASS_FLASH is not set
+CONFIG_LEDS_CLASS_MULTICOLOR=y
+# CONFIG_LEDS_CR0014114 is not set
+# CONFIG_LEDS_DAC124S085 is not set
+# CONFIG_LEDS_EL15203000 is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_LEDS_GROUP_MULTICOLOR is not set
+# CONFIG_LEDS_INTEL_SS4200 is not set
+# CONFIG_LEDS_IS31FL319X is not set
+# CONFIG_LEDS_IS31FL32XX is not set
+# CONFIG_LEDS_LM3530 is not set
+# CONFIG_LEDS_LM3532 is not set
+# CONFIG_LEDS_LM355x is not set
+# CONFIG_LEDS_LM3642 is not set
+# CONFIG_LEDS_LM3692X is not set
+# CONFIG_LEDS_LM3697 is not set
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_LP3952 is not set
+# CONFIG_LEDS_LP50XX is not set
+# CONFIG_LEDS_LP5521 is not set
+# CONFIG_LEDS_LP5523 is not set
+# CONFIG_LEDS_LP5562 is not set
+# CONFIG_LEDS_LP55XX_COMMON is not set
+# CONFIG_LEDS_LP8501 is not set
+# CONFIG_LEDS_LP8860 is not set
+# CONFIG_LEDS_LT3593 is not set
+# CONFIG_LEDS_MLXCPLD is not set
+# CONFIG_LEDS_MLXREG is not set
+# CONFIG_LEDS_NIC78BX is not set
+# CONFIG_LEDS_NS2 is not set
+# CONFIG_LEDS_OT200 is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_PCA963X is not set
+# CONFIG_LEDS_PCA995X is not set
+# CONFIG_LEDS_PWM is not set
+# CONFIG_LEDS_PWM_MULTICOLOR is not set
+# CONFIG_LEDS_REGULATOR is not set
+# CONFIG_LEDS_SPI_BYTE is not set
+# CONFIG_LEDS_SYSCON is not set
+# CONFIG_LEDS_TCA6507 is not set
+# CONFIG_LEDS_TI_LMU_COMMON is not set
+# CONFIG_LEDS_TLC591XX is not set
+CONFIG_LEDS_TRIGGERS=y
+# CONFIG_LEDS_TRIGGER_ACTIVITY is not set
+# CONFIG_LEDS_TRIGGER_AUDIO is not set
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+# CONFIG_LEDS_TRIGGER_CAMERA is not set
+# CONFIG_LEDS_TRIGGER_CPU is not set
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+# CONFIG_LEDS_TRIGGER_DISK is not set
+# CONFIG_LEDS_TRIGGER_GPIO is not set
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+# CONFIG_LEDS_TRIGGER_MTD is not set
+CONFIG_LEDS_TRIGGER_NETDEV=y
+# CONFIG_LEDS_TRIGGER_ONESHOT is not set
+# CONFIG_LEDS_TRIGGER_PANIC is not set
+# CONFIG_LEDS_TRIGGER_PATTERN is not set
+CONFIG_LEDS_TRIGGER_TIMER=y
+# CONFIG_LEDS_TRIGGER_TRANSIENT is not set
+# CONFIG_LEDS_TRIGGER_TTY is not set
+# CONFIG_LEDS_TURRIS_OMNIA is not set
+# CONFIG_LEDS_USER is not set
+# CONFIG_LED_TRIGGER_PHY is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_LEGACY_TIOCSTI is not set
+# CONFIG_LIB80211 is not set
+# CONFIG_LIB80211_CRYPT_CCMP is not set
+# CONFIG_LIB80211_CRYPT_TKIP is not set
+# CONFIG_LIB80211_CRYPT_WEP is not set
+# CONFIG_LIB80211_DEBUG is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_LIBERTAS_THINFIRM is not set
+# CONFIG_LIBERTAS_USB is not set
+# CONFIG_LIBFC is not set
+# CONFIG_LIBFCOE is not set
+# CONFIG_LIBIPW_DEBUG is not set
+# CONFIG_LIBNVDIMM is not set
+# CONFIG_LIDAR_LITE_V2 is not set
+CONFIG_LINEAR_RANGES=y
+# CONFIG_LIQUIDIO is not set
+# CONFIG_LIQUIDIO_VF is not set
+# CONFIG_LIRC is not set
+CONFIG_LIST_HARDENED=y
+# CONFIG_LITEX_LITEETH is not set
+# CONFIG_LITEX_SOC_CONTROLLER is not set
+# CONFIG_LIVEPATCH is not set
+# CONFIG_LKDTM is not set
+CONFIG_LLC=y
+# CONFIG_LLC2 is not set
+# CONFIG_LMK04832 is not set
+# CONFIG_LMP91000 is not set
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_LOCKD is not set
+CONFIG_LOCKDEP_BITS=15
+CONFIG_LOCKDEP_CHAINS_BITS=16
+CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS=12
+CONFIG_LOCKDEP_STACK_TRACE_BITS=19
+CONFIG_LOCKDEP_STACK_TRACE_HASH_BITS=14
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_LOCKD_V4=y
+# CONFIG_LOCKUP_DETECTOR is not set
+# CONFIG_LOCK_EVENT_COUNTS is not set
+CONFIG_LOCK_MM_AND_FIND_VMA=y
+# CONFIG_LOCK_STAT is not set
+# CONFIG_LOCK_TORTURE_TEST is not set
+# CONFIG_LOGIG940_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIWHEELS_FF is not set
+# CONFIG_LOGO is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=12
+# CONFIG_LOONGSON_MC146818 is not set
+# CONFIG_LPC_ICH is not set
+# CONFIG_LPC_SCH is not set
+# CONFIG_LP_CONSOLE is not set
+CONFIG_LRU_GEN=y
+CONFIG_LRU_GEN_ENABLED=y
+# CONFIG_LRU_GEN_STATS is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity"
+CONFIG_LSM_MMAP_MIN_ADDR=65536
+# CONFIG_LTC1660 is not set
+# CONFIG_LTC2471 is not set
+# CONFIG_LTC2485 is not set
+# CONFIG_LTC2496 is not set
+# CONFIG_LTC2497 is not set
+# CONFIG_LTC2632 is not set
+# CONFIG_LTC2688 is not set
+# CONFIG_LTC2983 is not set
+# CONFIG_LTE_GDM724X is not set
+CONFIG_LTO_NONE=y
+# CONFIG_LTR501 is not set
+# CONFIG_LTRF216A is not set
+# CONFIG_LV0104CS is not set
+# CONFIG_LWTUNNEL is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_LZ4HC_COMPRESS is not set
+# CONFIG_LZ4_COMPRESS is not set
+# CONFIG_LZ4_DECOMPRESS is not set
+CONFIG_LZMA_COMPRESS=y
+CONFIG_LZMA_DECOMPRESS=y
+# CONFIG_LZO_COMPRESS is not set
+# CONFIG_LZO_DECOMPRESS is not set
+# CONFIG_M62332 is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_MAC80211_MESSAGE_TRACING is not set
+CONFIG_MAC80211_STA_HASH_MAX_SIZE=0
+# CONFIG_MACB is not set
+# CONFIG_MACH_ASM9260 is not set
+# CONFIG_MACH_DECSTATION is not set
+# CONFIG_MACH_INGENIC is not set
+# CONFIG_MACH_INGENIC_SOC is not set
+# CONFIG_MACH_JAZZ is not set
+# CONFIG_MACH_JZ4740 is not set
+# CONFIG_MACH_LOONGSON2EF is not set
+# CONFIG_MACH_LOONGSON32 is not set
+# CONFIG_MACH_LOONGSON64 is not set
+# CONFIG_MACH_NINTENDO64 is not set
+# CONFIG_MACH_PIC32 is not set
+# CONFIG_MACH_REALTEK_RTL is not set
+# CONFIG_MACH_TX49XX is not set
+# CONFIG_MACINTOSH_DRIVERS is not set
+# CONFIG_MACSEC is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_MACVTAP is not set
+# CONFIG_MAC_EMUMOUSEBTN is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MAG3110 is not set
+# CONFIG_MAGIC_SYSRQ is not set
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
+# CONFIG_MAGIC_SYSRQ_SERIAL is not set
+CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
+# CONFIG_MAILBOX is not set
+# CONFIG_MANAGER_SBS is not set
+# CONFIG_MANGLE_BOOTARGS is not set
+# CONFIG_MARVELL_10G_PHY is not set
+# CONFIG_MARVELL_88Q2XXX_PHY is not set
+# CONFIG_MARVELL_88X2222_PHY is not set
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_MAX1027 is not set
+# CONFIG_MAX11100 is not set
+# CONFIG_MAX1118 is not set
+# CONFIG_MAX11205 is not set
+# CONFIG_MAX11410 is not set
+# CONFIG_MAX1241 is not set
+# CONFIG_MAX1363 is not set
+# CONFIG_MAX30100 is not set
+# CONFIG_MAX30102 is not set
+# CONFIG_MAX30208 is not set
+# CONFIG_MAX31827 is not set
+# CONFIG_MAX31856 is not set
+# CONFIG_MAX31865 is not set
+# CONFIG_MAX44000 is not set
+# CONFIG_MAX44009 is not set
+# CONFIG_MAX517 is not set
+# CONFIG_MAX5432 is not set
+# CONFIG_MAX5481 is not set
+# CONFIG_MAX5487 is not set
+# CONFIG_MAX5522 is not set
+# CONFIG_MAX5821 is not set
+# CONFIG_MAX63XX_WATCHDOG is not set
+# CONFIG_MAX9611 is not set
+# CONFIG_MAXIM_THERMOCOUPLE is not set
+# CONFIG_MAXLINEAR_GPHY is not set
+CONFIG_MAX_SKB_FRAGS=17
+# CONFIG_MB1232 is not set
+# CONFIG_MC3230 is not set
+# CONFIG_MCB is not set
+# CONFIG_MCP320X is not set
+# CONFIG_MCP3422 is not set
+# CONFIG_MCP3911 is not set
+# CONFIG_MCP4018 is not set
+# CONFIG_MCP41010 is not set
+# CONFIG_MCP4131 is not set
+# CONFIG_MCP4531 is not set
+# CONFIG_MCP4725 is not set
+# CONFIG_MCP4728 is not set
+# CONFIG_MCP4922 is not set
+# CONFIG_MCPM is not set
+# CONFIG_MCTP is not set
+# CONFIG_MD is not set
+# CONFIG_MDIO_BCM_UNIMAC is not set
+# CONFIG_MDIO_BITBANG is not set
+# CONFIG_MDIO_BUS_MUX_GPIO is not set
+# CONFIG_MDIO_BUS_MUX_MMIOREG is not set
+# CONFIG_MDIO_BUS_MUX_MULTIPLEXER is not set
+# CONFIG_MDIO_DEVICE is not set
+# CONFIG_MDIO_DEVRES is not set
+# CONFIG_MDIO_HISI_FEMAC is not set
+# CONFIG_MDIO_IPQ4019 is not set
+# CONFIG_MDIO_IPQ8064 is not set
+# CONFIG_MDIO_MSCC_MIIM is not set
+# CONFIG_MDIO_MVUSB is not set
+# CONFIG_MDIO_OCTEON is not set
+# CONFIG_MDIO_THUNDER is not set
+# CONFIG_MDM_GCC_9607 is not set
+# CONFIG_MD_BITMAP_FILE is not set
+# CONFIG_MD_FAULTY is not set
+# CONFIG_MEDIATEK_GE_PHY is not set
+# CONFIG_MEDIATEK_MT6577_AUXADC is not set
+# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set
+# CONFIG_MEDIA_ATTACH is not set
+# CONFIG_MEDIA_CAMERA_SUPPORT is not set
+# CONFIG_MEDIA_CEC_SUPPORT is not set
+# CONFIG_MEDIA_CONTROLLER is not set
+# CONFIG_MEDIA_CONTROLLER_DVB is not set
+# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set
+# CONFIG_MEDIA_PCI_SUPPORT is not set
+# CONFIG_MEDIA_PLATFORM_DRIVERS is not set
+# CONFIG_MEDIA_PLATFORM_SUPPORT is not set
+# CONFIG_MEDIA_RADIO_SUPPORT is not set
+# CONFIG_MEDIA_SDR_SUPPORT is not set
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+# CONFIG_MEDIA_SUPPORT is not set
+# CONFIG_MEDIA_SUPPORT_FILTER is not set
+# CONFIG_MEDIA_TEST_SUPPORT is not set
+# CONFIG_MEDIA_TUNER_E4000 is not set
+# CONFIG_MEDIA_TUNER_FC0011 is not set
+# CONFIG_MEDIA_TUNER_FC0012 is not set
+# CONFIG_MEDIA_TUNER_FC0013 is not set
+# CONFIG_MEDIA_TUNER_FC2580 is not set
+# CONFIG_MEDIA_TUNER_IT913X is not set
+# CONFIG_MEDIA_TUNER_M88RS6000T is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MSI001 is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2063 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MXL301RF is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_QM1D1B0004 is not set
+# CONFIG_MEDIA_TUNER_QM1D1C0042 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_R820T is not set
+# CONFIG_MEDIA_TUNER_SI2157 is not set
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA18212 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+# CONFIG_MEDIA_TUNER_TDA18250 is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_TUA9001 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC4000 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_USB_SUPPORT is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_MELLANOX_PLATFORM is not set
+CONFIG_MEMBARRIER=y
+CONFIG_MEMFD_CREATE=y
+# CONFIG_MEMORY is not set
+# CONFIG_MEMORY_FAILURE is not set
+# CONFIG_MEMORY_HOTPLUG is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_MEMTEST is not set
+# CONFIG_MEN_A21_WDT is not set
+# CONFIG_MESON_SM is not set
+CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
+# CONFIG_MFD_88PM800 is not set
+# CONFIG_MFD_88PM805 is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_AAT2870_CORE is not set
+# CONFIG_MFD_AC100 is not set
+# CONFIG_MFD_ACT8945A is not set
+# CONFIG_MFD_ARIZONA_I2C is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_AS3711 is not set
+# CONFIG_MFD_AS3722 is not set
+# CONFIG_MFD_ATC260X_I2C is not set
+# CONFIG_MFD_ATMEL_FLEXCOM is not set
+# CONFIG_MFD_ATMEL_HLCDC is not set
+# CONFIG_MFD_AXP20X is not set
+# CONFIG_MFD_AXP20X_I2C is not set
+# CONFIG_MFD_BCM590XX is not set
+# CONFIG_MFD_BD9571MWV is not set
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_CPCAP is not set
+# CONFIG_MFD_CS42L43_I2C is not set
+# CONFIG_MFD_CS5535 is not set
+# CONFIG_MFD_DA9052_I2C is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_DA9055 is not set
+# CONFIG_MFD_DA9062 is not set
+# CONFIG_MFD_DA9063 is not set
+# CONFIG_MFD_DA9150 is not set
+# CONFIG_MFD_DLN2 is not set
+# CONFIG_MFD_EXYNOS_LPASS is not set
+# CONFIG_MFD_GATEWORKS_GSC is not set
+# CONFIG_MFD_HI6421_PMIC is not set
+# CONFIG_MFD_INTEL_M10_BMC_SPI is not set
+# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set
+# CONFIG_MFD_IQS62X is not set
+# CONFIG_MFD_JANZ_CMODIO is not set
+# CONFIG_MFD_KEMPLD is not set
+# CONFIG_MFD_LM3533 is not set
+# CONFIG_MFD_LOCHNAGAR is not set
+# CONFIG_MFD_LP3943 is not set
+# CONFIG_MFD_LP8788 is not set
+# CONFIG_MFD_MADERA is not set
+# CONFIG_MFD_MAX14577 is not set
+# CONFIG_MFD_MAX5970 is not set
+# CONFIG_MFD_MAX77541 is not set
+# CONFIG_MFD_MAX77620 is not set
+# CONFIG_MFD_MAX77650 is not set
+# CONFIG_MFD_MAX77686 is not set
+# CONFIG_MFD_MAX77693 is not set
+# CONFIG_MFD_MAX77714 is not set
+# CONFIG_MFD_MAX77843 is not set
+# CONFIG_MFD_MAX8907 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_MFD_MC13XXX is not set
+# CONFIG_MFD_MC13XXX_I2C is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_MENF21BMC is not set
+# CONFIG_MFD_MP2629 is not set
+# CONFIG_MFD_MT6360 is not set
+# CONFIG_MFD_MT6370 is not set
+# CONFIG_MFD_MT6397 is not set
+# CONFIG_MFD_NTXEC is not set
+# CONFIG_MFD_OCELOT is not set
+# CONFIG_MFD_OMAP_USB_HOST is not set
+# CONFIG_MFD_PALMAS is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_PM8XXX is not set
+# CONFIG_MFD_QCOM_PM8008 is not set
+# CONFIG_MFD_RC5T583 is not set
+# CONFIG_MFD_RDC321X is not set
+# CONFIG_MFD_RETU is not set
+# CONFIG_MFD_RK8XX_I2C is not set
+# CONFIG_MFD_RK8XX_SPI is not set
+# CONFIG_MFD_RN5T618 is not set
+# CONFIG_MFD_ROHM_BD71828 is not set
+# CONFIG_MFD_ROHM_BD718XX is not set
+# CONFIG_MFD_ROHM_BD957XMUF is not set
+# CONFIG_MFD_RSMU_I2C is not set
+# CONFIG_MFD_RSMU_SPI is not set
+# CONFIG_MFD_RT4831 is not set
+# CONFIG_MFD_RT5033 is not set
+# CONFIG_MFD_RT5120 is not set
+# CONFIG_MFD_SEC_CORE is not set
+# CONFIG_MFD_SI476X_CORE is not set
+# CONFIG_MFD_SKY81452 is not set
+# CONFIG_MFD_SL28CPLD is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_SMPRO is not set
+# CONFIG_MFD_STMFX is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_STPMIC1 is not set
+# CONFIG_MFD_SY7636A is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TIMBERDALE is not set
+# CONFIG_MFD_TI_AM335X_TSCADC is not set
+# CONFIG_MFD_TI_LMU is not set
+# CONFIG_MFD_TI_LP873X is not set
+# CONFIG_MFD_TI_LP87565 is not set
+# CONFIG_MFD_TPS65086 is not set
+# CONFIG_MFD_TPS65090 is not set
+# CONFIG_MFD_TPS65217 is not set
+# CONFIG_MFD_TPS65218 is not set
+# CONFIG_MFD_TPS65219 is not set
+# CONFIG_MFD_TPS6586X is not set
+# CONFIG_MFD_TPS65910 is not set
+# CONFIG_MFD_TPS65912 is not set
+# CONFIG_MFD_TPS65912_I2C is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_TPS6594_I2C is not set
+# CONFIG_MFD_TPS6594_SPI is not set
+# CONFIG_MFD_TQMX86 is not set
+# CONFIG_MFD_VIPERBOARD is not set
+# CONFIG_MFD_VX855 is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_MFD_WM831X is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MHI_BUS is not set
+# CONFIG_MHI_BUS_DEBUG is not set
+# CONFIG_MHI_BUS_EP is not set
+# CONFIG_MHI_BUS_PCI_GENERIC is not set
+# CONFIG_MHI_NET is not set
+# CONFIG_MHI_WWAN_CTRL is not set
+# CONFIG_MHI_WWAN_MBIM is not set
+# CONFIG_MICREL_KS8995MA is not set
+# CONFIG_MICREL_PHY is not set
+# CONFIG_MICROCHIP_PHY is not set
+# CONFIG_MICROCHIP_PIT64B is not set
+# CONFIG_MICROCHIP_T1S_PHY is not set
+# CONFIG_MICROCHIP_T1_PHY is not set
+# CONFIG_MICROSEMI_PHY is not set
+# CONFIG_MIGRATION is not set
+CONFIG_MII=y
+# CONFIG_MIKROTIK is not set
+# CONFIG_MIKROTIK_RB532 is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_MINIX_FS_NATIVE_ENDIAN is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_MIPS32_N32 is not set
+# CONFIG_MIPS32_O32 is not set
+# CONFIG_MIPS_ALCHEMY is not set
+# CONFIG_MIPS_CDMM is not set
+# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set
+# CONFIG_MIPS_CMDLINE_FROM_DTB is not set
+# CONFIG_MIPS_COBALT is not set
+# CONFIG_MIPS_CPS is not set
+# CONFIG_MIPS_ELF_APPENDED_DTB is not set
+# CONFIG_MIPS_FP_SUPPORT is not set
+# CONFIG_MIPS_GENERIC is not set
+# CONFIG_MIPS_GENERIC_KERNEL is not set
+# CONFIG_MIPS_MALTA is not set
+# CONFIG_MIPS_O32_FP64_SUPPORT is not set
+# CONFIG_MIPS_PLATFORM_DEVICES is not set
+# CONFIG_MIPS_RAW_APPENDED_DTB is not set
+# CONFIG_MIPS_VA_BITS_48 is not set
+# CONFIG_MIPS_VPE_LOADER is not set
+# CONFIG_MISC_ALCOR_PCI is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_MISC_RTSX_PCI is not set
+# CONFIG_MISC_RTSX_USB is not set
+# CONFIG_MISDN is not set
+# CONFIG_MISDN_AVMFRITZ is not set
+# CONFIG_MISDN_HFCPCI is not set
+# CONFIG_MISDN_HFCUSB is not set
+# CONFIG_MISDN_INFINEON is not set
+# CONFIG_MISDN_NETJET is not set
+# CONFIG_MISDN_SPEEDFAX is not set
+# CONFIG_MISDN_W6692 is not set
+CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY=y
+# CONFIG_MKISS is not set
+# CONFIG_MLX4_CORE is not set
+# CONFIG_MLX4_EN is not set
+# CONFIG_MLX5_CORE is not set
+# CONFIG_MLX5_MACSEC is not set
+# CONFIG_MLX5_SF is not set
+# CONFIG_MLX5_VFIO_PCI is not set
+# CONFIG_MLX90614 is not set
+# CONFIG_MLX90632 is not set
+# CONFIG_MLXFW is not set
+# CONFIG_MLXSW_CORE is not set
+# CONFIG_MLX_PLATFORM is not set
+# CONFIG_MMA7455_I2C is not set
+# CONFIG_MMA7455_SPI is not set
+# CONFIG_MMA7660 is not set
+# CONFIG_MMA8452 is not set
+# CONFIG_MMA9551 is not set
+# CONFIG_MMA9553 is not set
+# CONFIG_MMC is not set
+# CONFIG_MMC35240 is not set
+# CONFIG_MMC_ARMMMCI is not set
+# CONFIG_MMC_AU1X is not set
+# CONFIG_MMC_BLOCK is not set
+CONFIG_MMC_BLOCK_MINORS=8
+# CONFIG_MMC_CAVIUM_THUNDERX is not set
+# CONFIG_MMC_CB710 is not set
+# CONFIG_MMC_CQHCI is not set
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_DW is not set
+# CONFIG_MMC_HSQ is not set
+# CONFIG_MMC_JZ4740 is not set
+# CONFIG_MMC_MTK is not set
+# CONFIG_MMC_MVSDIO is not set
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SDHCI_ACPI is not set
+# CONFIG_MMC_SDHCI_AM654 is not set
+# CONFIG_MMC_SDHCI_BCM_KONA is not set
+# CONFIG_MMC_SDHCI_BRCMSTB is not set
+# CONFIG_MMC_SDHCI_CADENCE is not set
+# CONFIG_MMC_SDHCI_F_SDH30 is not set
+# CONFIG_MMC_SDHCI_IPROC is not set
+# CONFIG_MMC_SDHCI_MILBEAUT is not set
+# CONFIG_MMC_SDHCI_MSM is not set
+# CONFIG_MMC_SDHCI_OF_ARASAN is not set
+# CONFIG_MMC_SDHCI_OF_ASPEED is not set
+# CONFIG_MMC_SDHCI_OF_AT91 is not set
+# CONFIG_MMC_SDHCI_OF_DWCMSHC is not set
+# CONFIG_MMC_SDHCI_OF_ESDHC is not set
+# CONFIG_MMC_SDHCI_OF_HLWD is not set
+# CONFIG_MMC_SDHCI_OMAP is not set
+# CONFIG_MMC_SDHCI_PXAV2 is not set
+# CONFIG_MMC_SDHCI_PXAV3 is not set
+# CONFIG_MMC_SDHCI_S3C is not set
+# CONFIG_MMC_SDHCI_XENON is not set
+# CONFIG_MMC_SDRICOH_CS is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_STM32_SDMMC is not set
+# CONFIG_MMC_TEST is not set
+# CONFIG_MMC_TIFM_SD is not set
+# CONFIG_MMC_TOSHIBA_PCI is not set
+# CONFIG_MMC_USDHI6ROL0 is not set
+# CONFIG_MMC_USHC is not set
+# CONFIG_MMC_VIA_SDMMC is not set
+# CONFIG_MMC_VUB300 is not set
+# CONFIG_MMIOTRACE is not set
+CONFIG_MMU=y
+CONFIG_MMU_GATHER_RCU_TABLE_FREE=y
+CONFIG_MMU_GATHER_TABLE_FREE=y
+CONFIG_MODPROBE_PATH="/sbin/modprobe"
+CONFIG_MODULES=y
+# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set
+# CONFIG_MODULE_COMPRESS_GZIP is not set
+CONFIG_MODULE_COMPRESS_NONE=y
+# CONFIG_MODULE_COMPRESS_XZ is not set
+# CONFIG_MODULE_COMPRESS_ZSTD is not set
+# CONFIG_MODULE_DEBUG is not set
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODULE_SIG is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_MODULE_STRIPPED=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_UNLOAD_TAINT_TRACKING is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MOST is not set
+# CONFIG_MOTORCOMM_PHY is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_ELAN_I2C is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_MOUSE_INPORT is not set
+# CONFIG_MOUSE_LOGIBM is not set
+# CONFIG_MOUSE_PC110PAD is not set
+# CONFIG_MOUSE_PS2_FOCALTECH is not set
+# CONFIG_MOUSE_PS2_SENTELIC is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_MOUSE_SYNAPTICS_USB is not set
+# CONFIG_MOXTET is not set
+# CONFIG_MPL115 is not set
+# CONFIG_MPL115_I2C is not set
+# CONFIG_MPL115_SPI is not set
+# CONFIG_MPL3115 is not set
+# CONFIG_MPLS is not set
+# CONFIG_MPLS_IPTUNNEL is not set
+# CONFIG_MPLS_ROUTING is not set
+# CONFIG_MPRLS0025PA is not set
+# CONFIG_MPTCP is not set
+# CONFIG_MPU3050_I2C is not set
+# CONFIG_MQ_IOSCHED_DEADLINE is not set
+# CONFIG_MQ_IOSCHED_KYBER is not set
+# CONFIG_MS5611 is not set
+# CONFIG_MS5637 is not set
+# CONFIG_MSA311 is not set
+# CONFIG_MSCC_OCELOT_SWITCH is not set
+# CONFIG_MSDOS_FS is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_MSE102X is not set
+# CONFIG_MSI_BITMAP_SELFTEST is not set
+# CONFIG_MSI_LAPTOP is not set
+# CONFIG_MSM_GCC_8953 is not set
+# CONFIG_MSM_MMCC_8994 is not set
+# CONFIG_MST_IRQ is not set
+CONFIG_MTD=y
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_MTD_BLOCK2MTD is not set
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_CMDLINE_PARTS is not set
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_DOCG3 is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_HYPERBUS is not set
+# CONFIG_MTD_IMPA7 is not set
+# CONFIG_MTD_INTEL_VR_NOR is not set
+# CONFIG_MTD_JEDECPROBE is not set
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_LPDDR2_NVM is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MCHP23K256 is not set
+# CONFIG_MTD_MCHP48L640 is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_MYLOADER_PARTS is not set
+# CONFIG_MTD_NAND_AMS_DELTA is not set
+# CONFIG_MTD_NAND_ARASAN is not set
+# CONFIG_MTD_NAND_ATMEL is not set
+# CONFIG_MTD_NAND_AU1550 is not set
+# CONFIG_MTD_NAND_BRCMNAND is not set
+# CONFIG_MTD_NAND_BRCMNAND_BCM63XX is not set
+# CONFIG_MTD_NAND_BRCMNAND_BCMBCA is not set
+# CONFIG_MTD_NAND_BRCMNAND_BRCMSTB is not set
+# CONFIG_MTD_NAND_BRCMNAND_IPROC is not set
+# CONFIG_MTD_NAND_CADENCE is not set
+# CONFIG_MTD_NAND_CAFE is not set
+# CONFIG_MTD_NAND_CS553X is not set
+# CONFIG_MTD_NAND_DAVINCI is not set
+# CONFIG_MTD_NAND_DENALI is not set
+# CONFIG_MTD_NAND_DENALI_DT is not set
+# CONFIG_MTD_NAND_DENALI_PCI is not set
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_ECC is not set
+# CONFIG_MTD_NAND_ECC_MXIC is not set
+# CONFIG_MTD_NAND_ECC_SW_BCH is not set
+# CONFIG_MTD_NAND_ECC_SW_HAMMING is not set
+# CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC is not set
+# CONFIG_MTD_NAND_FSL_ELBC is not set
+# CONFIG_MTD_NAND_FSL_IFC is not set
+# CONFIG_MTD_NAND_FSL_UPM is not set
+# CONFIG_MTD_NAND_FSMC is not set
+# CONFIG_MTD_NAND_GPIO is not set
+# CONFIG_MTD_NAND_GPMI_NAND is not set
+# CONFIG_MTD_NAND_HISI504 is not set
+# CONFIG_MTD_NAND_INTEL_LGM is not set
+# CONFIG_MTD_NAND_MPC5121_NFC is not set
+# CONFIG_MTD_NAND_MTK is not set
+# CONFIG_MTD_NAND_MTK_BMT is not set
+# CONFIG_MTD_NAND_MXC is not set
+# CONFIG_MTD_NAND_MXIC is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_NDFC is not set
+# CONFIG_MTD_NAND_OMAP2 is not set
+# CONFIG_MTD_NAND_OMAP_BCH_BUILD is not set
+# CONFIG_MTD_NAND_ORION is not set
+# CONFIG_MTD_NAND_PASEMI is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_NAND_RICOH is not set
+# CONFIG_MTD_NAND_S3C2410 is not set
+# CONFIG_MTD_NAND_SHARPSL is not set
+# CONFIG_MTD_NAND_SH_FLCTL is not set
+# CONFIG_MTD_NAND_SOCRATES is not set
+# CONFIG_MTD_NAND_TXX9NDFMC is not set
+CONFIG_MTD_OF_PARTS=y
+# CONFIG_MTD_ONENAND is not set
+# CONFIG_MTD_OOPS is not set
+# CONFIG_MTD_OTP is not set
+# CONFIG_MTD_PARSER_TRX is not set
+# CONFIG_MTD_PARTITIONED_MASTER is not set
+# CONFIG_MTD_PCI is not set
+# CONFIG_MTD_PCMCIA is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_PHYSMAP_GEMINI is not set
+# CONFIG_MTD_PHYSMAP_GPIO_ADDR is not set
+# CONFIG_MTD_PHYSMAP_IXP4XX is not set
+CONFIG_MTD_PHYSMAP_OF=y
+# CONFIG_MTD_PHYSMAP_VERSATILE is not set
+# CONFIG_MTD_PLATRAM is not set
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_RAW_NAND is not set
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_ROM is not set
+CONFIG_MTD_ROOTFS_ROOT_DEV=y
+# CONFIG_MTD_ROUTERBOOT_PARTS is not set
+# CONFIG_MTD_SERCOMM_PARTS is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_SM_COMMON is not set
+# CONFIG_MTD_SPI_NAND is not set
+# CONFIG_MTD_SPI_NOR is not set
+# CONFIG_MTD_SPI_NOR_SWP_DISABLE is not set
+CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE=y
+# CONFIG_MTD_SPI_NOR_SWP_KEEP is not set
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+# CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE is not set
+CONFIG_MTD_SPLIT=y
+# CONFIG_MTD_SPLIT_BCM63XX_FW is not set
+# CONFIG_MTD_SPLIT_BCM_WFI_FW is not set
+# CONFIG_MTD_SPLIT_BRNIMAGE_FW is not set
+# CONFIG_MTD_SPLIT_ELF_FW is not set
+# CONFIG_MTD_SPLIT_EVA_FW is not set
+# CONFIG_MTD_SPLIT_FIRMWARE is not set
+CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware"
+# CONFIG_MTD_SPLIT_FIT_FW is not set
+# CONFIG_MTD_SPLIT_H3C_VFS is not set
+# CONFIG_MTD_SPLIT_JIMAGE_FW is not set
+# CONFIG_MTD_SPLIT_LZMA_FW is not set
+# CONFIG_MTD_SPLIT_MINOR_FW is not set
+# CONFIG_MTD_SPLIT_SEAMA_FW is not set
+# CONFIG_MTD_SPLIT_SEIL_FW is not set
+CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y
+CONFIG_MTD_SPLIT_SUPPORT=y
+# CONFIG_MTD_SPLIT_TPLINK_FW is not set
+# CONFIG_MTD_SPLIT_TRX_FW is not set
+# CONFIG_MTD_SPLIT_UIMAGE_FW is not set
+# CONFIG_MTD_SPLIT_WRGG_FW is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_MTD_SWAP is not set
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_UBI is not set
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+# CONFIG_MTD_UBI_NVMEM is not set
+# CONFIG_MTD_VIRT_CONCAT is not set
+# CONFIG_MTK_DEVAPC is not set
+# CONFIG_MTK_MMSYS is not set
+# CONFIG_MTK_T7XX is not set
+# CONFIG_MTK_THERMAL is not set
+# CONFIG_MULTIPLEXER is not set
+CONFIG_MULTIUSER=y
+# CONFIG_MUTEX_SPIN_ON_OWNER is not set
+# CONFIG_MUX_ADG792A is not set
+# CONFIG_MUX_ADGS1408 is not set
+# CONFIG_MUX_GPIO is not set
+# CONFIG_MUX_MMIO is not set
+# CONFIG_MV643XX_ETH is not set
+# CONFIG_MVMDIO is not set
+# CONFIG_MVNETA_BM is not set
+# CONFIG_MV_XOR_V2 is not set
+# CONFIG_MWAVE is not set
+# CONFIG_MWL8K is not set
+# CONFIG_MXC4005 is not set
+# CONFIG_MXC6255 is not set
+# CONFIG_MYRI10GE is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NAU7802 is not set
+# CONFIG_NBPFAXI_DMA is not set
+# CONFIG_NCN26000_PHY is not set
+# CONFIG_NE2000 is not set
+# CONFIG_NE2K_PCI is not set
+CONFIG_NET=y
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETCONSOLE_EXTENDED_LOG is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVSIM is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_NETFILTER_ADVANCED is not set
+# CONFIG_NETFILTER_EGRESS is not set
+# CONFIG_NETFILTER_INGRESS is not set
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NETFILTER_NETLINK_ACCT is not set
+# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set
+# CONFIG_NETFILTER_NETLINK_HOOK is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NETFILTER_NETLINK_OSF is not set
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_XTABLES is not set
+# CONFIG_NETFILTER_XTABLES_COMPAT is not set
+# CONFIG_NETFILTER_XT_CONNMARK is not set
+# CONFIG_NETFILTER_XT_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_BPF is not set
+# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set
+# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set
+# CONFIG_NETFILTER_XT_MATCH_CONNTRACK is not set
+# CONFIG_NETFILTER_XT_MATCH_CPU is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ECN is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_HELPER is not set
+# CONFIG_NETFILTER_XT_MATCH_HL is not set
+# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set
+# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
+# CONFIG_NETFILTER_XT_MATCH_L2TP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set
+# CONFIG_NETFILTER_XT_MATCH_OSF is not set
+# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
+# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_RECENT is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set
+# CONFIG_NETFILTER_XT_MATCH_STATE is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set
+# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_CT is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_HL is not set
+# CONFIG_NETFILTER_XT_TARGET_HMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set
+# CONFIG_NETFILTER_XT_TARGET_LED is not set
+# CONFIG_NETFILTER_XT_TARGET_LOG is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+# CONFIG_NETFILTER_XT_TARGET_REDIRECT is not set
+# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+# CONFIG_NETFILTER_XT_TARGET_TEE is not set
+# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFS_STATS is not set
+# CONFIG_NETLABEL is not set
+# CONFIG_NETLINK_DIAG is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NETROM is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETXEN_NIC is not set
+# CONFIG_NET_9P is not set
+# CONFIG_NET_ACT_BPF is not set
+# CONFIG_NET_ACT_CSUM is not set
+# CONFIG_NET_ACT_CT is not set
+# CONFIG_NET_ACT_GACT is not set
+# CONFIG_NET_ACT_GATE is not set
+# CONFIG_NET_ACT_IFE is not set
+# CONFIG_NET_ACT_IPT is not set
+# CONFIG_NET_ACT_MIRRED is not set
+# CONFIG_NET_ACT_MPLS is not set
+# CONFIG_NET_ACT_NAT is not set
+# CONFIG_NET_ACT_PEDIT is not set
+# CONFIG_NET_ACT_POLICE is not set
+# CONFIG_NET_ACT_SAMPLE is not set
+# CONFIG_NET_ACT_SIMP is not set
+# CONFIG_NET_ACT_SKBEDIT is not set
+# CONFIG_NET_ACT_SKBMOD is not set
+# CONFIG_NET_ACT_TUNNEL_KEY is not set
+# CONFIG_NET_ACT_VLAN is not set
+# CONFIG_NET_CALXEDA_XGMAC is not set
+CONFIG_NET_CLS=y
+# CONFIG_NET_CLS_ACT is not set
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_BPF is not set
+# CONFIG_NET_CLS_FLOW is not set
+# CONFIG_NET_CLS_FLOWER is not set
+# CONFIG_NET_CLS_FW is not set
+# CONFIG_NET_CLS_MATCHALL is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_U32 is not set
+CONFIG_NET_CORE=y
+# CONFIG_NET_DEVLINK is not set
+# CONFIG_NET_DEV_REFCNT_TRACKER is not set
+# CONFIG_NET_DROP_MONITOR is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_NET_DSA_AR9331 is not set
+# CONFIG_NET_DSA_BCM_SF2 is not set
+# CONFIG_NET_DSA_LANTIQ_GSWIP is not set
+# CONFIG_NET_DSA_LOOP is not set
+# CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON is not set
+# CONFIG_NET_DSA_MSCC_FELIX is not set
+# CONFIG_NET_DSA_MSCC_OCELOT_EXT is not set
+# CONFIG_NET_DSA_MSCC_SEVILLE is not set
+# CONFIG_NET_DSA_MT7530 is not set
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6XXX is not set
+# CONFIG_NET_DSA_MV88E6XXX_LEDS is not set
+# CONFIG_NET_DSA_MV88E6XXX_PTP is not set
+# CONFIG_NET_DSA_QCA8K is not set
+# CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT is not set
+# CONFIG_NET_DSA_REALTEK is not set
+# CONFIG_NET_DSA_REALTEK_SMI is not set
+# CONFIG_NET_DSA_SJA1105 is not set
+# CONFIG_NET_DSA_SMSC_LAN9303_I2C is not set
+# CONFIG_NET_DSA_SMSC_LAN9303_MDIO is not set
+# CONFIG_NET_DSA_TAG_AR9331 is not set
+# CONFIG_NET_DSA_TAG_BRCM is not set
+# CONFIG_NET_DSA_TAG_BRCM_LEGACY is not set
+# CONFIG_NET_DSA_TAG_BRCM_PREPEND is not set
+# CONFIG_NET_DSA_TAG_DSA is not set
+# CONFIG_NET_DSA_TAG_EDSA is not set
+# CONFIG_NET_DSA_TAG_GSWIP is not set
+# CONFIG_NET_DSA_TAG_HELLCREEK is not set
+# CONFIG_NET_DSA_TAG_KSZ is not set
+# CONFIG_NET_DSA_TAG_LAN9303 is not set
+# CONFIG_NET_DSA_TAG_MTK is not set
+# CONFIG_NET_DSA_TAG_NONE is not set
+# CONFIG_NET_DSA_TAG_OCELOT is not set
+# CONFIG_NET_DSA_TAG_OCELOT_8021Q is not set
+# CONFIG_NET_DSA_TAG_QCA is not set
+# CONFIG_NET_DSA_TAG_RTL4_A is not set
+# CONFIG_NET_DSA_TAG_RTL8_4 is not set
+# CONFIG_NET_DSA_TAG_RZN1_A5PSW is not set
+# CONFIG_NET_DSA_TAG_SJA1105 is not set
+# CONFIG_NET_DSA_TAG_TRAILER is not set
+# CONFIG_NET_DSA_TAG_XRS700X is not set
+# CONFIG_NET_DSA_VITESSE_VSC73XX is not set
+# CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM is not set
+# CONFIG_NET_DSA_VITESSE_VSC73XX_SPI is not set
+# CONFIG_NET_DSA_XRS700X_I2C is not set
+# CONFIG_NET_DSA_XRS700X_MDIO is not set
+# CONFIG_NET_EMATCH is not set
+# CONFIG_NET_EMATCH_CANID is not set
+# CONFIG_NET_EMATCH_CMP is not set
+# CONFIG_NET_EMATCH_IPT is not set
+# CONFIG_NET_EMATCH_META is not set
+# CONFIG_NET_EMATCH_NBYTE is not set
+CONFIG_NET_EMATCH_STACK=32
+# CONFIG_NET_EMATCH_TEXT is not set
+# CONFIG_NET_EMATCH_U32 is not set
+# CONFIG_NET_FAILOVER is not set
+# CONFIG_NET_FC is not set
+# CONFIG_NET_FOU is not set
+# CONFIG_NET_FOU_IP_TUNNELS is not set
+# CONFIG_NET_IFE is not set
+# CONFIG_NET_IPGRE is not set
+CONFIG_NET_IPGRE_BROADCAST=y
+# CONFIG_NET_IPGRE_DEMUX is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPVTI is not set
+# CONFIG_NET_IP_TUNNEL is not set
+# CONFIG_NET_KEY is not set
+# CONFIG_NET_KEY_MIGRATE is not set
+# CONFIG_NET_L3_MASTER_DEV is not set
+# CONFIG_NET_MEDIATEK_STAR_EMAC is not set
+# CONFIG_NET_MPLS_GSO is not set
+# CONFIG_NET_NCSI is not set
+# CONFIG_NET_NSH is not set
+# CONFIG_NET_NS_REFCNT_TRACKER is not set
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_NET_PTP_CLASSIFY is not set
+CONFIG_NET_RX_BUSY_POLL=y
+# CONFIG_NET_SB1000 is not set
+CONFIG_NET_SCHED=y
+# CONFIG_NET_SCH_CAKE is not set
+# CONFIG_NET_SCH_CBS is not set
+# CONFIG_NET_SCH_CHOKE is not set
+# CONFIG_NET_SCH_CODEL is not set
+CONFIG_NET_SCH_DEFAULT=y
+# CONFIG_NET_SCH_DRR is not set
+# CONFIG_NET_SCH_ETF is not set
+# CONFIG_NET_SCH_ETS is not set
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_NET_SCH_FQ is not set
+CONFIG_NET_SCH_FQ_CODEL=y
+# CONFIG_NET_SCH_FQ_PIE is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_HHF is not set
+# CONFIG_NET_SCH_HTB is not set
+# CONFIG_NET_SCH_INGRESS is not set
+# CONFIG_NET_SCH_MQPRIO is not set
+# CONFIG_NET_SCH_MULTIQ is not set
+# CONFIG_NET_SCH_NETEM is not set
+# CONFIG_NET_SCH_PIE is not set
+# CONFIG_NET_SCH_PLUG is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_QFQ is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFB is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_SKBPRIO is not set
+# CONFIG_NET_SCH_TAPRIO is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SELFTESTS is not set
+CONFIG_NET_SOCK_MSG=y
+# CONFIG_NET_SWITCHDEV is not set
+# CONFIG_NET_TC_SKB_EXT is not set
+# CONFIG_NET_TEAM is not set
+# CONFIG_NET_TULIP is not set
+# CONFIG_NET_UDP_TUNNEL is not set
+CONFIG_NET_VENDOR_3COM=y
+CONFIG_NET_VENDOR_8390=y
+CONFIG_NET_VENDOR_ADAPTEC=y
+CONFIG_NET_VENDOR_ADI=y
+CONFIG_NET_VENDOR_AGERE=y
+CONFIG_NET_VENDOR_ALACRITECH=y
+CONFIG_NET_VENDOR_ALTEON=y
+CONFIG_NET_VENDOR_AMAZON=y
+CONFIG_NET_VENDOR_AMD=y
+CONFIG_NET_VENDOR_AQUANTIA=y
+CONFIG_NET_VENDOR_ARC=y
+# CONFIG_NET_VENDOR_ASIX is not set
+CONFIG_NET_VENDOR_ATHEROS=y
+CONFIG_NET_VENDOR_BROADCOM=y
+CONFIG_NET_VENDOR_BROCADE=y
+CONFIG_NET_VENDOR_CADENCE=y
+CONFIG_NET_VENDOR_CAVIUM=y
+CONFIG_NET_VENDOR_CHELSIO=y
+CONFIG_NET_VENDOR_CIRRUS=y
+CONFIG_NET_VENDOR_CISCO=y
+CONFIG_NET_VENDOR_CORTINA=y
+CONFIG_NET_VENDOR_DAVICOM=y
+CONFIG_NET_VENDOR_DEC=y
+CONFIG_NET_VENDOR_DLINK=y
+CONFIG_NET_VENDOR_EMULEX=y
+# CONFIG_NET_VENDOR_ENGLEDER is not set
+CONFIG_NET_VENDOR_EZCHIP=y
+CONFIG_NET_VENDOR_FARADAY=y
+CONFIG_NET_VENDOR_FREESCALE=y
+CONFIG_NET_VENDOR_FUJITSU=y
+# CONFIG_NET_VENDOR_FUNGIBLE is not set
+CONFIG_NET_VENDOR_GOOGLE=y
+CONFIG_NET_VENDOR_HISILICON=y
+CONFIG_NET_VENDOR_HUAWEI=y
+CONFIG_NET_VENDOR_I825XX=y
+CONFIG_NET_VENDOR_IBM=y
+CONFIG_NET_VENDOR_INTEL=y
+# CONFIG_NET_VENDOR_LITEX is not set
+CONFIG_NET_VENDOR_MARVELL=y
+CONFIG_NET_VENDOR_MELLANOX=y
+CONFIG_NET_VENDOR_MICREL=y
+CONFIG_NET_VENDOR_MICROCHIP=y
+CONFIG_NET_VENDOR_MICROSEMI=y
+# CONFIG_NET_VENDOR_MICROSOFT is not set
+CONFIG_NET_VENDOR_MYRI=y
+CONFIG_NET_VENDOR_NATSEMI=y
+CONFIG_NET_VENDOR_NETERION=y
+CONFIG_NET_VENDOR_NETRONOME=y
+CONFIG_NET_VENDOR_NI=y
+CONFIG_NET_VENDOR_NVIDIA=y
+CONFIG_NET_VENDOR_OKI=y
+CONFIG_NET_VENDOR_PACKET_ENGINES=y
+CONFIG_NET_VENDOR_PENSANDO=y
+CONFIG_NET_VENDOR_QLOGIC=y
+CONFIG_NET_VENDOR_QUALCOMM=y
+CONFIG_NET_VENDOR_RDC=y
+CONFIG_NET_VENDOR_REALTEK=y
+CONFIG_NET_VENDOR_RENESAS=y
+CONFIG_NET_VENDOR_ROCKER=y
+CONFIG_NET_VENDOR_SAMSUNG=y
+CONFIG_NET_VENDOR_SEEQ=y
+CONFIG_NET_VENDOR_SILAN=y
+CONFIG_NET_VENDOR_SIS=y
+CONFIG_NET_VENDOR_SMSC=y
+CONFIG_NET_VENDOR_SOCIONEXT=y
+CONFIG_NET_VENDOR_SOLARFLARE=y
+CONFIG_NET_VENDOR_STMICRO=y
+CONFIG_NET_VENDOR_SUN=y
+CONFIG_NET_VENDOR_SYNOPSYS=y
+CONFIG_NET_VENDOR_TEHUTI=y
+CONFIG_NET_VENDOR_TI=y
+CONFIG_NET_VENDOR_TOSHIBA=y
+# CONFIG_NET_VENDOR_VERTEXCOM is not set
+CONFIG_NET_VENDOR_VIA=y
+# CONFIG_NET_VENDOR_WANGXUN is not set
+CONFIG_NET_VENDOR_WIZNET=y
+CONFIG_NET_VENDOR_XILINX=y
+CONFIG_NET_VENDOR_XIRCOM=y
+# CONFIG_NET_VRF is not set
+# CONFIG_NET_XGENE is not set
+CONFIG_NEW_LEDS=y
+# CONFIG_NFC is not set
+# CONFIG_NFP is not set
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V2 is not set
+# CONFIG_NFSD_V2_ACL is not set
+# CONFIG_NFSD_V3_ACL is not set
+# CONFIG_NFSD_V4 is not set
+# CONFIG_NFS_ACL_SUPPORT is not set
+CONFIG_NFS_COMMON=y
+# CONFIG_NFS_DISABLE_UDP_SUPPORT is not set
+# CONFIG_NFS_FS is not set
+# CONFIG_NFS_FSCACHE is not set
+# CONFIG_NFS_SWAP is not set
+# CONFIG_NFS_V2 is not set
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_V4_1 is not set
+# CONFIG_NFTL is not set
+# CONFIG_NFT_BRIDGE_META is not set
+# CONFIG_NFT_BRIDGE_REJECT is not set
+# CONFIG_NFT_CONNLIMIT is not set
+# CONFIG_NFT_DUP_IPV4 is not set
+# CONFIG_NFT_DUP_IPV6 is not set
+# CONFIG_NFT_FIB_IPV4 is not set
+# CONFIG_NFT_FIB_IPV6 is not set
+# CONFIG_NFT_FIB_NETDEV is not set
+# CONFIG_NFT_FLOW_OFFLOAD is not set
+# CONFIG_NFT_OSF is not set
+# CONFIG_NFT_REJECT_NETDEV is not set
+# CONFIG_NFT_SOCKET is not set
+# CONFIG_NFT_SYNPROXY is not set
+# CONFIG_NFT_TPROXY is not set
+# CONFIG_NFT_TUNNEL is not set
+# CONFIG_NFT_XFRM is not set
+# CONFIG_NF_CONNTRACK is not set
+# CONFIG_NF_CONNTRACK_AMANDA is not set
+# CONFIG_NF_CONNTRACK_BRIDGE is not set
+# CONFIG_NF_CONNTRACK_EVENTS is not set
+# CONFIG_NF_CONNTRACK_FTP is not set
+# CONFIG_NF_CONNTRACK_H323 is not set
+# CONFIG_NF_CONNTRACK_IRC is not set
+# CONFIG_NF_CONNTRACK_LABELS is not set
+# CONFIG_NF_CONNTRACK_MARK is not set
+# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set
+# CONFIG_NF_CONNTRACK_PPTP is not set
+CONFIG_NF_CONNTRACK_PROCFS=y
+# CONFIG_NF_CONNTRACK_SANE is not set
+# CONFIG_NF_CONNTRACK_SECMARK is not set
+# CONFIG_NF_CONNTRACK_SIP is not set
+# CONFIG_NF_CONNTRACK_SNMP is not set
+# CONFIG_NF_CONNTRACK_TFTP is not set
+# CONFIG_NF_CONNTRACK_TIMEOUT is not set
+# CONFIG_NF_CONNTRACK_TIMESTAMP is not set
+# CONFIG_NF_CONNTRACK_ZONES is not set
+# CONFIG_NF_CT_NETLINK is not set
+# CONFIG_NF_CT_NETLINK_HELPER is not set
+# CONFIG_NF_CT_NETLINK_TIMEOUT is not set
+# CONFIG_NF_CT_PROTO_DCCP is not set
+# CONFIG_NF_CT_PROTO_GRE is not set
+# CONFIG_NF_CT_PROTO_SCTP is not set
+# CONFIG_NF_CT_PROTO_UDPLITE is not set
+# CONFIG_NF_DEFRAG_IPV4 is not set
+# CONFIG_NF_DUP_IPV4 is not set
+# CONFIG_NF_DUP_IPV6 is not set
+# CONFIG_NF_FLOW_TABLE is not set
+# CONFIG_NF_FLOW_TABLE_PROCFS is not set
+# CONFIG_NF_LOG_ARP is not set
+# CONFIG_NF_LOG_IPV4 is not set
+# CONFIG_NF_LOG_SYSLOG is not set
+# CONFIG_NF_NAT is not set
+# CONFIG_NF_NAT_AMANDA is not set
+# CONFIG_NF_NAT_FTP is not set
+# CONFIG_NF_NAT_H323 is not set
+# CONFIG_NF_NAT_IRC is not set
+# CONFIG_NF_NAT_PPTP is not set
+# CONFIG_NF_NAT_SIP is not set
+# CONFIG_NF_NAT_SNMP_BASIC is not set
+# CONFIG_NF_NAT_TFTP is not set
+# CONFIG_NF_REJECT_IPV4 is not set
+# CONFIG_NF_REJECT_IPV6 is not set
+# CONFIG_NF_SOCKET_IPV4 is not set
+# CONFIG_NF_SOCKET_IPV6 is not set
+# CONFIG_NF_TABLES is not set
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_TABLES_BRIDGE=y
+CONFIG_NF_TABLES_INET=y
+CONFIG_NF_TABLES_IPV4=y
+CONFIG_NF_TABLES_IPV6=y
+CONFIG_NF_TABLES_NETDEV=y
+# CONFIG_NF_TPROXY_IPV4 is not set
+# CONFIG_NF_TPROXY_IPV6 is not set
+# CONFIG_NGBE is not set
+# CONFIG_NI903X_WDT is not set
+# CONFIG_NIC7018_WDT is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_NIU is not set
+# CONFIG_NI_XGE_MANAGEMENT_ENET is not set
+CONFIG_NLATTR=y
+# CONFIG_NLMON is not set
+# CONFIG_NLS is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+# CONFIG_NLS_UCS2_UTILS is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_NOA1305 is not set
+# CONFIG_NOP_USB_XCEIV is not set
+# CONFIG_NORTEL_HERMES is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+# CONFIG_NOZOMI is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_NO_HZ_FULL is not set
+# CONFIG_NO_HZ_IDLE is not set
+# CONFIG_NS83820 is not set
+# CONFIG_NTB is not set
+# CONFIG_NTFS3_64BIT_CLUSTER is not set
+# CONFIG_NTFS3_FS is not set
+# CONFIG_NTFS3_FS_POSIX_ACL is not set
+# CONFIG_NTFS3_LZX_XPRESS is not set
+# CONFIG_NTFS_DEBUG is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_NTP_PPS is not set
+# CONFIG_NULL_TTY is not set
+# CONFIG_NUMA is not set
+# CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set
+# CONFIG_NVMEM is not set
+# CONFIG_NVMEM_BCM_OCOTP is not set
+# CONFIG_NVMEM_BLOCK is not set
+# CONFIG_NVMEM_IMX_OCOTP is not set
+# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
+# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
+# CONFIG_NVMEM_LAYOUT_U_BOOT_ENV is not set
+# CONFIG_NVMEM_REBOOT_MODE is not set
+# CONFIG_NVMEM_RMEM is not set
+# CONFIG_NVMEM_SYSFS is not set
+# CONFIG_NVMEM_U_BOOT_ENV is not set
+# CONFIG_NVME_AUTH is not set
+# CONFIG_NVME_FC is not set
+# CONFIG_NVME_TARGET is not set
+# CONFIG_NVME_TCP is not set
+# CONFIG_NVME_VERBOSE_ERRORS is not set
+# CONFIG_NVRAM is not set
+# CONFIG_NV_TCO is not set
+# CONFIG_NXP_C45_TJA11XX_PHY is not set
+# CONFIG_NXP_CBTX_PHY is not set
+# CONFIG_NXP_TJA11XX_PHY is not set
+# CONFIG_N_GSM is not set
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_OBS600 is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_OCTEONTX2_AF is not set
+# CONFIG_OCTEONTX2_PF is not set
+# CONFIG_OCTEON_EP is not set
+# CONFIG_OF_OVERLAY is not set
+CONFIG_OF_PARTITION=y
+CONFIG_OF_RESERVED_MEM=y
+# CONFIG_OF_UNITTEST is not set
+# CONFIG_OID_REGISTRY is not set
+# CONFIG_OMAP2_DSS_DEBUG is not set
+# CONFIG_OMAP2_DSS_DEBUGFS is not set
+# CONFIG_OMAP2_DSS_SDI is not set
+# CONFIG_OMAP_OCP2SCP is not set
+# CONFIG_OMAP_USB2 is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_OPENVSWITCH is not set
+# CONFIG_OPEN_DICE is not set
+# CONFIG_OPT3001 is not set
+# CONFIG_OPT4001 is not set
+# CONFIG_ORANGEFS_FS is not set
+# CONFIG_ORION_WATCHDOG is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_OSNOISE_TRACER is not set
+CONFIG_OVERLAY_FS=y
+# CONFIG_OVERLAY_FS_DEBUG is not set
+# CONFIG_OVERLAY_FS_INDEX is not set
+# CONFIG_OVERLAY_FS_METACOPY is not set
+CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y
+# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set
+CONFIG_OVERLAY_FS_XINO_AUTO=y
+# CONFIG_P54_COMMON is not set
+# CONFIG_PA12203001 is not set
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+# CONFIG_PACKING is not set
+# CONFIG_PAGE_EXTENSION is not set
+# CONFIG_PAGE_OWNER is not set
+# CONFIG_PAGE_POISONING is not set
+# CONFIG_PAGE_POOL is not set
+# CONFIG_PAGE_POOL_STATS is not set
+# CONFIG_PAGE_REPORTING is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_32KB is not set
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_64KB is not set
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_TABLE_CHECK is not set
+# CONFIG_PALMAS_GPADC is not set
+# CONFIG_PANASONIC_LAPTOP is not set
+# CONFIG_PANEL is not set
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PANIC_ON_OOPS_VALUE=1
+CONFIG_PANIC_TIMEOUT=1
+# CONFIG_PANTHERLORD_FF is not set
+# CONFIG_PARAVIRT is not set
+# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set
+# CONFIG_PARPORT is not set
+# CONFIG_PARPORT_1284 is not set
+# CONFIG_PARPORT_GSC is not set
+# CONFIG_PARPORT_PC is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_PATA_ACPI is not set
+# CONFIG_PATA_ALI is not set
+# CONFIG_PATA_AMD is not set
+# CONFIG_PATA_ARASAN_CF is not set
+# CONFIG_PATA_ARTOP is not set
+# CONFIG_PATA_ATIIXP is not set
+# CONFIG_PATA_ATP867X is not set
+# CONFIG_PATA_CMD640_PCI is not set
+# CONFIG_PATA_CMD64X is not set
+# CONFIG_PATA_CS5520 is not set
+# CONFIG_PATA_CS5530 is not set
+# CONFIG_PATA_CS5535 is not set
+# CONFIG_PATA_CS5536 is not set
+# CONFIG_PATA_CYPRESS is not set
+# CONFIG_PATA_EFAR is not set
+# CONFIG_PATA_HPT366 is not set
+# CONFIG_PATA_HPT37X is not set
+# CONFIG_PATA_HPT3X2N is not set
+# CONFIG_PATA_HPT3X3 is not set
+# CONFIG_PATA_IMX is not set
+# CONFIG_PATA_ISAPNP is not set
+# CONFIG_PATA_IT8213 is not set
+# CONFIG_PATA_IT821X is not set
+# CONFIG_PATA_JMICRON is not set
+# CONFIG_PATA_LEGACY is not set
+# CONFIG_PATA_MARVELL is not set
+# CONFIG_PATA_MPIIX is not set
+# CONFIG_PATA_NETCELL is not set
+# CONFIG_PATA_NINJA32 is not set
+# CONFIG_PATA_NS87410 is not set
+# CONFIG_PATA_NS87415 is not set
+# CONFIG_PATA_OCTEON_CF is not set
+# CONFIG_PATA_OF_PLATFORM is not set
+# CONFIG_PATA_OLDPIIX is not set
+# CONFIG_PATA_OPTI is not set
+# CONFIG_PATA_OPTIDMA is not set
+# CONFIG_PATA_PARPORT is not set
+# CONFIG_PATA_PCMCIA is not set
+# CONFIG_PATA_PDC2027X is not set
+# CONFIG_PATA_PDC_OLD is not set
+# CONFIG_PATA_PLATFORM is not set
+# CONFIG_PATA_QDI is not set
+# CONFIG_PATA_RADISYS is not set
+# CONFIG_PATA_RDC is not set
+# CONFIG_PATA_RZ1000 is not set
+# CONFIG_PATA_SC1200 is not set
+# CONFIG_PATA_SCH is not set
+# CONFIG_PATA_SERVERWORKS is not set
+# CONFIG_PATA_SIL680 is not set
+# CONFIG_PATA_SIS is not set
+# CONFIG_PATA_TOSHIBA is not set
+# CONFIG_PATA_TRIFLEX is not set
+# CONFIG_PATA_VIA is not set
+# CONFIG_PATA_WINBOND is not set
+# CONFIG_PATA_WINBOND_VLB is not set
+# CONFIG_PC104 is not set
+# CONFIG_PC300TOO is not set
+# CONFIG_PCCARD is not set
+# CONFIG_PCH_DMA is not set
+# CONFIG_PCH_GBE is not set
+# CONFIG_PCH_PHUB is not set
+# CONFIG_PCI is not set
+# CONFIG_PCI200SYN is not set
+# CONFIG_PCIEAER is not set
+# CONFIG_PCIEAER_INJECT is not set
+# CONFIG_PCIEASPM is not set
+# CONFIG_PCIEPORTBUS is not set
+# CONFIG_PCIE_AL is not set
+# CONFIG_PCIE_ALTERA is not set
+# CONFIG_PCIE_ARMADA_8K is not set
+CONFIG_PCIE_BUS_DEFAULT=y
+# CONFIG_PCIE_BUS_PEER2PEER is not set
+# CONFIG_PCIE_BUS_PERFORMANCE is not set
+# CONFIG_PCIE_BUS_SAFE is not set
+# CONFIG_PCIE_BUS_TUNE_OFF is not set
+# CONFIG_PCIE_CADENCE_HOST is not set
+# CONFIG_PCIE_CADENCE_PLAT_HOST is not set
+# CONFIG_PCIE_DPC is not set
+# CONFIG_PCIE_DW_PLAT is not set
+# CONFIG_PCIE_DW_PLAT_HOST is not set
+# CONFIG_PCIE_ECRC is not set
+# CONFIG_PCIE_IPROC is not set
+# CONFIG_PCIE_KIRIN is not set
+# CONFIG_PCIE_LAYERSCAPE_GEN4 is not set
+# CONFIG_PCIE_MEDIATEK_GEN3 is not set
+# CONFIG_PCIE_MICROCHIP_HOST is not set
+# CONFIG_PCIE_PTM is not set
+# CONFIG_PCIE_XILINX is not set
+# CONFIG_PCIPCWATCHDOG is not set
+# CONFIG_PCI_ATMEL is not set
+# CONFIG_PCI_CNB20LE_QUIRK is not set
+# CONFIG_PCI_DEBUG is not set
+# CONFIG_PCI_DISABLE_COMMON_QUIRKS is not set
+# CONFIG_PCI_DYNAMIC_OF_NODES is not set
+# CONFIG_PCI_ENDPOINT is not set
+# CONFIG_PCI_ENDPOINT_TEST is not set
+# CONFIG_PCI_FTPCI100 is not set
+# CONFIG_PCI_HERMES is not set
+# CONFIG_PCI_HISI is not set
+# CONFIG_PCI_HOST_GENERIC is not set
+# CONFIG_PCI_HOST_THUNDER_ECAM is not set
+# CONFIG_PCI_HOST_THUNDER_PEM is not set
+# CONFIG_PCI_IOV is not set
+# CONFIG_PCI_J721E_HOST is not set
+# CONFIG_PCI_LAYERSCAPE is not set
+# CONFIG_PCI_MESON is not set
+# CONFIG_PCI_MSI is not set
+# CONFIG_PCI_PASID is not set
+# CONFIG_PCI_PF_STUB is not set
+# CONFIG_PCI_PRI is not set
+CONFIG_PCI_QUIRKS=y
+# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set
+# CONFIG_PCI_STUB is not set
+# CONFIG_PCI_SW_SWITCHTEC is not set
+CONFIG_PCI_SYSCALL=y
+# CONFIG_PCI_V3_SEMI is not set
+# CONFIG_PCI_XGENE is not set
+# CONFIG_PCMCIA is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_AHA152X is not set
+# CONFIG_PCMCIA_ATMEL is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_PCMCIA_DEBUG is not set
+# CONFIG_PCMCIA_FDOMAIN is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+# CONFIG_PCMCIA_HERMES is not set
+# CONFIG_PCMCIA_LOAD_CIS is not set
+# CONFIG_PCMCIA_NINJA_SCSI is not set
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_QLOGIC is not set
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_SPECTRUM is not set
+# CONFIG_PCMCIA_SYM53C500 is not set
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_XIRCOM is not set
+# CONFIG_PCNET32 is not set
+# CONFIG_PCPU_DEV_REFCNT is not set
+CONFIG_PCP_BATCH_SCALE_MAX=5
+# CONFIG_PCSPKR_PLATFORM is not set
+# CONFIG_PCS_MTK_USXGMII is not set
+# CONFIG_PCS_XPCS is not set
+# CONFIG_PD6729 is not set
+# CONFIG_PDC_ADMA is not set
+# CONFIG_PDS_CORE is not set
+# CONFIG_PECI is not set
+# CONFIG_PERCPU_STATS is not set
+# CONFIG_PERCPU_TEST is not set
+# CONFIG_PERF_EVENTS is not set
+# CONFIG_PERF_EVENTS_AMD_POWER is not set
+# CONFIG_PERSISTENT_KEYRINGS is not set
+# CONFIG_PER_VMA_LOCK_STATS is not set
+# CONFIG_PHANTOM is not set
+# CONFIG_PHONET is not set
+# CONFIG_PHYLIB is not set
+# CONFIG_PHYLIB_LEDS is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+# CONFIG_PHY_BRCM_USB is not set
+# CONFIG_PHY_CADENCE_DPHY is not set
+# CONFIG_PHY_CADENCE_DPHY_RX is not set
+# CONFIG_PHY_CADENCE_SALVO is not set
+# CONFIG_PHY_CADENCE_SIERRA is not set
+# CONFIG_PHY_CADENCE_TORRENT is not set
+# CONFIG_PHY_CAN_TRANSCEIVER is not set
+# CONFIG_PHY_CPCAP_USB is not set
+# CONFIG_PHY_EXYNOS_DP_VIDEO is not set
+# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set
+# CONFIG_PHY_FSL_IMX8MQ_USB is not set
+# CONFIG_PHY_INGENIC_USB is not set
+# CONFIG_PHY_INTEL_KEEMBAY_EMMC is not set
+# CONFIG_PHY_LAN966X_SERDES is not set
+# CONFIG_PHY_MAPPHONE_MDM6600 is not set
+# CONFIG_PHY_MIXEL_MIPI_DPHY is not set
+# CONFIG_PHY_MTK_HDMI is not set
+# CONFIG_PHY_MTK_MIPI_DSI is not set
+# CONFIG_PHY_MTK_XFI_TPHY is not set
+# CONFIG_PHY_MVEBU_CP110_UTMI is not set
+# CONFIG_PHY_OCELOT_SERDES is not set
+# CONFIG_PHY_PISTACHIO_USB is not set
+# CONFIG_PHY_PXA_28NM_HSIC is not set
+# CONFIG_PHY_PXA_28NM_USB2 is not set
+# CONFIG_PHY_QCOM_USB_HS is not set
+# CONFIG_PHY_QCOM_USB_HSIC is not set
+# CONFIG_PHY_SAMSUNG_USB2 is not set
+# CONFIG_PHY_TUSB1210 is not set
+# CONFIG_PHY_XGENE is not set
+# CONFIG_PI433 is not set
+# CONFIG_PID_IN_CONTEXTIDR is not set
+# CONFIG_PID_NS is not set
+CONFIG_PINCONF=y
+# CONFIG_PINCTRL is not set
+# CONFIG_PINCTRL_AMD is not set
+# CONFIG_PINCTRL_AXP209 is not set
+# CONFIG_PINCTRL_CEDARFORK is not set
+# CONFIG_PINCTRL_CY8C95X0 is not set
+# CONFIG_PINCTRL_EXYNOS is not set
+# CONFIG_PINCTRL_ICELAKE is not set
+# CONFIG_PINCTRL_INGENIC is not set
+# CONFIG_PINCTRL_LPASS_LPI is not set
+# CONFIG_PINCTRL_MCP23S08 is not set
+# CONFIG_PINCTRL_MDM9607 is not set
+# CONFIG_PINCTRL_MICROCHIP_SGPIO is not set
+# CONFIG_PINCTRL_MSM8953 is not set
+# CONFIG_PINCTRL_MSM8X74 is not set
+# CONFIG_PINCTRL_MT6779 is not set
+# CONFIG_PINCTRL_MT8167 is not set
+# CONFIG_PINCTRL_MT8192 is not set
+# CONFIG_PINCTRL_MT8195 is not set
+# CONFIG_PINCTRL_MT8365 is not set
+# CONFIG_PINCTRL_MTK_V2 is not set
+# CONFIG_PINCTRL_OCELOT is not set
+# CONFIG_PINCTRL_PISTACHIO is not set
+# CONFIG_PINCTRL_SC7280 is not set
+# CONFIG_PINCTRL_SC8180X is not set
+# CONFIG_PINCTRL_SDX55 is not set
+CONFIG_PINCTRL_SINGLE=y
+# CONFIG_PINCTRL_SM6115 is not set
+# CONFIG_PINCTRL_SM6125 is not set
+# CONFIG_PINCTRL_SM8350 is not set
+# CONFIG_PINCTRL_STMFX is not set
+# CONFIG_PINCTRL_SX150X is not set
+# CONFIG_PING is not set
+CONFIG_PINMUX=y
+# CONFIG_PKCS7_MESSAGE_PARSER is not set
+# CONFIG_PL310_ERRATA_588369 is not set
+# CONFIG_PL310_ERRATA_727915 is not set
+# CONFIG_PL310_ERRATA_753970 is not set
+# CONFIG_PL310_ERRATA_769419 is not set
+# CONFIG_PL320_MBOX is not set
+# CONFIG_PL330_DMA is not set
+# CONFIG_PLATFORM_MHU is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_PLIP is not set
+# CONFIG_PLX_DMA is not set
+# CONFIG_PLX_HERMES is not set
+# CONFIG_PM is not set
+# CONFIG_PMBUS is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_PMS7003 is not set
+# CONFIG_PM_AUTOSLEEP is not set
+# CONFIG_PM_DEBUG is not set
+# CONFIG_PM_DEVFREQ is not set
+# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
+# CONFIG_PM_WAKELOCKS is not set
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_POSIX_TIMERS=y
+# CONFIG_POWERCAP is not set
+# CONFIG_POWER_RESET is not set
+# CONFIG_POWER_RESET_BRCMKONA is not set
+# CONFIG_POWER_RESET_BRCMSTB is not set
+# CONFIG_POWER_RESET_GPIO is not set
+# CONFIG_POWER_RESET_GPIO_RESTART is not set
+# CONFIG_POWER_RESET_LINKSTATION is not set
+# CONFIG_POWER_RESET_LTC2952 is not set
+# CONFIG_POWER_RESET_PIIX4_POWEROFF is not set
+# CONFIG_POWER_RESET_QNAP is not set
+# CONFIG_POWER_RESET_REGULATOR is not set
+# CONFIG_POWER_RESET_RESTART is not set
+# CONFIG_POWER_RESET_SYSCON is not set
+# CONFIG_POWER_RESET_SYSCON_POWEROFF is not set
+# CONFIG_POWER_RESET_VERSATILE is not set
+# CONFIG_POWER_RESET_XGENE is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_POWER_SUPPLY_HWMON is not set
+# CONFIG_PPC4xx_GPIO is not set
+# CONFIG_PPC_16K_PAGES is not set
+# CONFIG_PPC_256K_PAGES is not set
+CONFIG_PPC_4K_PAGES=y
+# CONFIG_PPC_64K_PAGES is not set
+# CONFIG_PPC_DISABLE_WERROR is not set
+# CONFIG_PPC_EMULATED_STATS is not set
+# CONFIG_PPC_EPAPR_HV_BYTECHAN is not set
+# CONFIG_PPC_QUEUED_SPINLOCKS is not set
+# CONFIG_PPP is not set
+# CONFIG_PPPOATM is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOE_HASH_BITS_1 is not set
+# CONFIG_PPPOE_HASH_BITS_2 is not set
+CONFIG_PPPOE_HASH_BITS_4=y
+# CONFIG_PPPOE_HASH_BITS_8 is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_PPP_ASYNC is not set
+# CONFIG_PPP_BSDCOMP is not set
+# CONFIG_PPP_DEFLATE is not set
+CONFIG_PPP_FILTER=y
+# CONFIG_PPP_MPPE is not set
+CONFIG_PPP_MULTILINK=y
+# CONFIG_PPP_SYNC_TTY is not set
+# CONFIG_PPS is not set
+# CONFIG_PPS_CLIENT_GPIO is not set
+# CONFIG_PPS_CLIENT_KTIMER is not set
+# CONFIG_PPS_CLIENT_LDISC is not set
+# CONFIG_PPS_CLIENT_PARPORT is not set
+# CONFIG_PPS_DEBUG is not set
+# CONFIG_PPTP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
+# CONFIG_PREEMPT_DYNAMIC is not set
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PRESTERA is not set
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_PRIME_NUMBERS is not set
+CONFIG_PRINTK=y
+# CONFIG_PRINTK_CALLER is not set
+# CONFIG_PRINTK_INDEX is not set
+# CONFIG_PRINTK_TIME is not set
+CONFIG_PRINT_STACK_DEPTH=64
+# CONFIG_PRISM2_USB is not set
+# CONFIG_PROC_CHILDREN is not set
+CONFIG_PROC_FS=y
+# CONFIG_PROC_KCORE is not set
+CONFIG_PROC_MEM_ALWAYS_FORCE=y
+# CONFIG_PROC_MEM_FORCE_PTRACE is not set
+# CONFIG_PROC_MEM_NO_FORCE is not set
+# CONFIG_PROC_PAGE_MONITOR is not set
+# CONFIG_PROC_STRIPPED is not set
+CONFIG_PROC_SYSCTL=y
+# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILING is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_PROVE_RAW_LOCK_NESTING is not set
+# CONFIG_PROVE_RCU is not set
+# CONFIG_PROVE_RCU_LIST is not set
+# CONFIG_PSAMPLE is not set
+# CONFIG_PSB6970_PHY is not set
+# CONFIG_PSE_CONTROLLER is not set
+# CONFIG_PSI is not set
+# CONFIG_PSI_DEFAULT_DISABLED is not set
+# CONFIG_PSTORE is not set
+# CONFIG_PSTORE_BLK is not set
+# CONFIG_PSTORE_COMPRESS is not set
+# CONFIG_PSTORE_CONSOLE is not set
+CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240
+# CONFIG_PSTORE_FTRACE is not set
+# CONFIG_PSTORE_PMSG is not set
+# CONFIG_PSTORE_RAM is not set
+# CONFIG_PTDUMP_DEBUGFS is not set
+# CONFIG_PTP_1588_CLOCK is not set
+# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set
+# CONFIG_PTP_1588_CLOCK_IDTCM is not set
+# CONFIG_PTP_1588_CLOCK_IXP46X is not set
+# CONFIG_PTP_1588_CLOCK_KVM is not set
+# CONFIG_PTP_1588_CLOCK_MOCK is not set
+# CONFIG_PTP_1588_CLOCK_OCP is not set
+# CONFIG_PTP_1588_CLOCK_PCH is not set
+# CONFIG_PTP_1588_CLOCK_VMW is not set
+# CONFIG_PVPANIC is not set
+# CONFIG_PWM is not set
+# CONFIG_PWM_ATMEL_TCB is not set
+# CONFIG_PWM_CLK is not set
+# CONFIG_PWM_DEBUG is not set
+# CONFIG_PWM_DWC is not set
+# CONFIG_PWM_FSL_FTM is not set
+# CONFIG_PWM_IMG is not set
+# CONFIG_PWM_JZ4740 is not set
+# CONFIG_PWM_MEDIATEK is not set
+# CONFIG_PWM_PCA9685 is not set
+# CONFIG_PWM_RASPBERRYPI_POE is not set
+# CONFIG_PWM_XILINX is not set
+CONFIG_PWRSEQ_EMMC=y
+# CONFIG_PWRSEQ_SD8787 is not set
+CONFIG_PWRSEQ_SIMPLE=y
+# CONFIG_QCA7000 is not set
+# CONFIG_QCA7000_SPI is not set
+# CONFIG_QCA7000_UART is not set
+# CONFIG_QCA807X_PHY is not set
+# CONFIG_QCA808X_PHY is not set
+# CONFIG_QCA83XX_PHY is not set
+# CONFIG_QCOM_A7PLL is not set
+# CONFIG_QCOM_BAM_DMUX is not set
+# CONFIG_QCOM_EMAC is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set
+# CONFIG_QCOM_GPI_DMA is not set
+# CONFIG_QCOM_HIDMA is not set
+# CONFIG_QCOM_HIDMA_MGMT is not set
+# CONFIG_QCOM_LMH is not set
+# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set
+# CONFIG_QCOM_SPMI_ADC5 is not set
+# CONFIG_QCOM_SPMI_ADC_TM5 is not set
+# CONFIG_QCOM_SPMI_IADC is not set
+# CONFIG_QCOM_SPMI_TEMP_ALARM is not set
+# CONFIG_QCOM_SPMI_VADC is not set
+# CONFIG_QCOM_SSC_BLOCK_BUS is not set
+# CONFIG_QED is not set
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QLA3XXX is not set
+# CONFIG_QLCNIC is not set
+# CONFIG_QLCNIC_DCB is not set
+# CONFIG_QLGE is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX6FS_FS is not set
+# CONFIG_QORIQ_CPUFREQ is not set
+# CONFIG_QORIQ_THERMAL is not set
+# CONFIG_QRTR is not set
+# CONFIG_QRTR_MHI is not set
+# CONFIG_QRTR_TUN is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_QUICC_ENGINE is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+# CONFIG_QUOTA_DEBUG is not set
+# CONFIG_QUOTA_NETLINK_INTERFACE is not set
+# CONFIG_R6040 is not set
+# CONFIG_R8169 is not set
+# CONFIG_R8169_LEDS is not set
+# CONFIG_R8712U is not set
+# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+# CONFIG_RAID6_PQ_BENCHMARK is not set
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_RALINK is not set
+# CONFIG_RANDOM32_SELFTEST is not set
+# CONFIG_RANDOMIZE_BASE is not set
+CONFIG_RANDOMIZE_KSTACK_OFFSET=y
+# CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set
+# CONFIG_RANDOM_KMALLOC_CACHES is not set
+# CONFIG_RANDSTRUCT_NONE is not set
+# CONFIG_RAPIDIO is not set
+# CONFIG_RAS is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_RCU_BOOST is not set
+# CONFIG_RCU_CPU_STALL_CPUTIME is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_RCU_EQS_DEBUG is not set
+# CONFIG_RCU_EXPERT is not set
+CONFIG_RCU_EXP_CPU_STALL_TIMEOUT=0
+CONFIG_RCU_NEED_SEGCBLIST=y
+# CONFIG_RCU_REF_SCALE_TEST is not set
+# CONFIG_RCU_SCALE_TEST is not set
+CONFIG_RCU_STALL_COMMON=y
+# CONFIG_RCU_STRICT_GRACE_PERIOD is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_RC_ATI_REMOTE is not set
+# CONFIG_RC_CORE is not set
+# CONFIG_RC_DECODERS is not set
+# CONFIG_RC_LOOPBACK is not set
+# CONFIG_RC_MAP is not set
+# CONFIG_RC_XBOX_DVD is not set
+# CONFIG_RDS is not set
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_GZIP is not set
+# CONFIG_RD_LZ4 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_ZSTD is not set
+# CONFIG_READABLE_ASM is not set
+# CONFIG_READ_ONLY_THP_FOR_FS is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_REDWOOD is not set
+# CONFIG_REED_SOLOMON is not set
+# CONFIG_REED_SOLOMON_DEC8 is not set
+# CONFIG_REED_SOLOMON_ENC8 is not set
+# CONFIG_REED_SOLOMON_TEST is not set
+# CONFIG_REGMAP is not set
+# CONFIG_REGMAP_I2C is not set
+# CONFIG_REGMAP_MMIO is not set
+# CONFIG_REGMAP_SPI is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_88PG86X is not set
+# CONFIG_REGULATOR_ACT8865 is not set
+# CONFIG_REGULATOR_AD5398 is not set
+# CONFIG_REGULATOR_ANATOP is not set
+# CONFIG_REGULATOR_AW37503 is not set
+# CONFIG_REGULATOR_DA9121 is not set
+# CONFIG_REGULATOR_DA9210 is not set
+# CONFIG_REGULATOR_DA9211 is not set
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FAN53555 is not set
+# CONFIG_REGULATOR_FAN53880 is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_GPIO is not set
+# CONFIG_REGULATOR_ISL6271A is not set
+# CONFIG_REGULATOR_ISL9305 is not set
+# CONFIG_REGULATOR_LP3971 is not set
+# CONFIG_REGULATOR_LP3972 is not set
+# CONFIG_REGULATOR_LP872X is not set
+# CONFIG_REGULATOR_LP8755 is not set
+# CONFIG_REGULATOR_LTC3589 is not set
+# CONFIG_REGULATOR_LTC3676 is not set
+# CONFIG_REGULATOR_MAX1586 is not set
+# CONFIG_REGULATOR_MAX20086 is not set
+# CONFIG_REGULATOR_MAX20411 is not set
+# CONFIG_REGULATOR_MAX77620 is not set
+# CONFIG_REGULATOR_MAX77826 is not set
+# CONFIG_REGULATOR_MAX77857 is not set
+# CONFIG_REGULATOR_MAX8649 is not set
+# CONFIG_REGULATOR_MAX8660 is not set
+# CONFIG_REGULATOR_MAX8893 is not set
+# CONFIG_REGULATOR_MAX8952 is not set
+# CONFIG_REGULATOR_MAX8973 is not set
+# CONFIG_REGULATOR_MCP16502 is not set
+# CONFIG_REGULATOR_MP5416 is not set
+# CONFIG_REGULATOR_MP8859 is not set
+# CONFIG_REGULATOR_MP886X is not set
+# CONFIG_REGULATOR_MPQ7920 is not set
+# CONFIG_REGULATOR_MT6311 is not set
+# CONFIG_REGULATOR_MT6315 is not set
+# CONFIG_REGULATOR_MT6359 is not set
+# CONFIG_REGULATOR_PCA9450 is not set
+# CONFIG_REGULATOR_PF8X00 is not set
+# CONFIG_REGULATOR_PFUZE100 is not set
+# CONFIG_REGULATOR_PV88060 is not set
+# CONFIG_REGULATOR_PV88080 is not set
+# CONFIG_REGULATOR_PV88090 is not set
+# CONFIG_REGULATOR_PWM is not set
+# CONFIG_REGULATOR_QCOM_REFGEN is not set
+# CONFIG_REGULATOR_RAA215300 is not set
+# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set
+# CONFIG_REGULATOR_RT4801 is not set
+# CONFIG_REGULATOR_RT4803 is not set
+# CONFIG_REGULATOR_RT5190A is not set
+# CONFIG_REGULATOR_RT5739 is not set
+# CONFIG_REGULATOR_RT5759 is not set
+# CONFIG_REGULATOR_RT6160 is not set
+# CONFIG_REGULATOR_RT6190 is not set
+# CONFIG_REGULATOR_RT6245 is not set
+# CONFIG_REGULATOR_RTMV20 is not set
+# CONFIG_REGULATOR_RTQ2134 is not set
+# CONFIG_REGULATOR_RTQ2208 is not set
+# CONFIG_REGULATOR_RTQ6752 is not set
+# CONFIG_REGULATOR_SLG51000 is not set
+# CONFIG_REGULATOR_SY8106A is not set
+# CONFIG_REGULATOR_SY8824X is not set
+# CONFIG_REGULATOR_SY8827N is not set
+# CONFIG_REGULATOR_TI_ABB is not set
+# CONFIG_REGULATOR_TPS51632 is not set
+# CONFIG_REGULATOR_TPS62360 is not set
+# CONFIG_REGULATOR_TPS6286X is not set
+# CONFIG_REGULATOR_TPS6287X is not set
+# CONFIG_REGULATOR_TPS65023 is not set
+# CONFIG_REGULATOR_TPS6507X is not set
+# CONFIG_REGULATOR_TPS65132 is not set
+# CONFIG_REGULATOR_TPS6524X is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+# CONFIG_REGULATOR_VCTRL is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_REISERFS_FS_POSIX_ACL is not set
+# CONFIG_REISERFS_FS_SECURITY is not set
+CONFIG_REISERFS_FS_XATTR=y
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_RELAY is not set
+# CONFIG_RELOCATABLE is not set
+CONFIG_RELR=y
+# CONFIG_REMOTEPROC is not set
+# CONFIG_RENESAS_PHY is not set
+# CONFIG_RESET_ATH79 is not set
+# CONFIG_RESET_BERLIN is not set
+# CONFIG_RESET_BRCMSTB is not set
+# CONFIG_RESET_BRCMSTB_RESCAL is not set
+# CONFIG_RESET_CONTROLLER is not set
+# CONFIG_RESET_IMX7 is not set
+# CONFIG_RESET_INTEL_GW is not set
+# CONFIG_RESET_LANTIQ is not set
+# CONFIG_RESET_LPC18XX is not set
+# CONFIG_RESET_MESON is not set
+# CONFIG_RESET_PISTACHIO is not set
+# CONFIG_RESET_SIMPLE is not set
+# CONFIG_RESET_SOCFPGA is not set
+# CONFIG_RESET_SUNXI is not set
+# CONFIG_RESET_TEGRA_BPMP is not set
+# CONFIG_RESET_TI_SYSCON is not set
+# CONFIG_RESET_TI_TPS380X is not set
+# CONFIG_RESET_ZYNQ is not set
+# CONFIG_RFD77402 is not set
+# CONFIG_RFD_FTL is not set
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_FULL is not set
+# CONFIG_RFKILL_GPIO is not set
+# CONFIG_RFKILL_INPUT is not set
+# CONFIG_RFKILL_LEDS is not set
+# CONFIG_RICHTEK_RTQ6056 is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_RING_BUFFER_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
+# CONFIG_RISCV_PMU is not set
+# CONFIG_RISCV_PMU_LEGACY is not set
+# CONFIG_RISCV_PMU_SBI is not set
+# CONFIG_RMI4_CORE is not set
+# CONFIG_RMNET is not set
+# CONFIG_ROCKCHIP_ERRATUM_3588001 is not set
+# CONFIG_ROCKCHIP_PHY is not set
+# CONFIG_ROCKER is not set
+# CONFIG_ROHM_BU27008 is not set
+# CONFIG_ROHM_BU27034 is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_ROSE is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1 is not set
+# CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2 is not set
+# CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA is not set
+# CONFIG_RPMSG_QCOM_GLINK_RPM is not set
+# CONFIG_RPMSG_VIRTIO is not set
+# CONFIG_RPMSG_WWAN_CTRL is not set
+# CONFIG_RPR0521 is not set
+# CONFIG_RSEQ is not set
+# CONFIG_RT2X00 is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_RTC_DEBUG is not set
+# CONFIG_RTC_DRV_ABB5ZES3 is not set
+# CONFIG_RTC_DRV_ABEOZ9 is not set
+# CONFIG_RTC_DRV_ABX80X is not set
+# CONFIG_RTC_DRV_ARMADA38X is not set
+# CONFIG_RTC_DRV_AU1XXX is not set
+# CONFIG_RTC_DRV_BQ32K is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_CADENCE is not set
+CONFIG_RTC_DRV_CMOS=y
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1302 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1307_CENTURY is not set
+# CONFIG_RTC_DRV_DS1343 is not set
+# CONFIG_RTC_DRV_DS1347 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_DS1685_FAMILY is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_DS2404 is not set
+# CONFIG_RTC_DRV_DS3232 is not set
+# CONFIG_RTC_DRV_EM3027 is not set
+# CONFIG_RTC_DRV_EP93XX is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_FTRTC010 is not set
+# CONFIG_RTC_DRV_GENERIC is not set
+# CONFIG_RTC_DRV_GOLDFISH is not set
+# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set
+# CONFIG_RTC_DRV_HYM8563 is not set
+# CONFIG_RTC_DRV_ISL12022 is not set
+# CONFIG_RTC_DRV_ISL12026 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_JZ4740 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_M41T93 is not set
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_MAX6916 is not set
+# CONFIG_RTC_DRV_MAX77686 is not set
+# CONFIG_RTC_DRV_MCP795 is not set
+# CONFIG_RTC_DRV_MOXART is not set
+# CONFIG_RTC_DRV_MPC5121 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_MT2712 is not set
+# CONFIG_RTC_DRV_NCT3018Y is not set
+# CONFIG_RTC_DRV_OMAP is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+# CONFIG_RTC_DRV_PCF2127 is not set
+# CONFIG_RTC_DRV_PCF85063 is not set
+# CONFIG_RTC_DRV_PCF8523 is not set
+# CONFIG_RTC_DRV_PCF85363 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_PL030 is not set
+# CONFIG_RTC_DRV_PL031 is not set
+# CONFIG_RTC_DRV_PS3 is not set
+# CONFIG_RTC_DRV_R7301 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_RV3028 is not set
+# CONFIG_RTC_DRV_RV3029C2 is not set
+# CONFIG_RTC_DRV_RV3032 is not set
+# CONFIG_RTC_DRV_RV8803 is not set
+# CONFIG_RTC_DRV_RX4581 is not set
+# CONFIG_RTC_DRV_RX6110 is not set
+# CONFIG_RTC_DRV_RX8010 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_SD3078 is not set
+# CONFIG_RTC_DRV_SNVS is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_SUN6I is not set
+# CONFIG_RTC_DRV_TEGRA is not set
+# CONFIG_RTC_DRV_TEST is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_XGENE is not set
+# CONFIG_RTC_DRV_ZYNQMP is not set
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_NVMEM is not set
+CONFIG_RTC_SYSTOHC=y
+CONFIG_RTC_SYSTOHC_DEVICE="rtc0"
+# CONFIG_RTL8180 is not set
+# CONFIG_RTL8187 is not set
+# CONFIG_RTL8192E is not set
+# CONFIG_RTL8192U is not set
+# CONFIG_RTL8306_PHY is not set
+# CONFIG_RTL8366RB_PHY is not set
+# CONFIG_RTL8366S_PHY is not set
+# CONFIG_RTL8366_SMI is not set
+# CONFIG_RTL8366_SMI_DEBUG_FS is not set
+# CONFIG_RTL8367B_PHY is not set
+# CONFIG_RTL8367_PHY is not set
+# CONFIG_RTLLIB is not set
+# CONFIG_RTL_CARDS is not set
+# CONFIG_RTS5208 is not set
+CONFIG_RT_MUTEXES=y
+CONFIG_RUNTIME_TESTING_MENU=y
+# CONFIG_RUST is not set
+# CONFIG_RV is not set
+CONFIG_RXKAD=y
+# CONFIG_RXPERF is not set
+# CONFIG_S2IO is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_SAMSUNG_LAPTOP is not set
+# CONFIG_SATA_ACARD_AHCI is not set
+# CONFIG_SATA_AHCI is not set
+# CONFIG_SATA_AHCI_PLATFORM is not set
+# CONFIG_SATA_DWC is not set
+# CONFIG_SATA_DWC_OLD_DMA is not set
+# CONFIG_SATA_FSL is not set
+# CONFIG_SATA_HIGHBANK is not set
+# CONFIG_SATA_HOST is not set
+# CONFIG_SATA_INIC162X is not set
+CONFIG_SATA_MOBILE_LPM_POLICY=0
+# CONFIG_SATA_MV is not set
+# CONFIG_SATA_NV is not set
+# CONFIG_SATA_PMP is not set
+# CONFIG_SATA_PROMISE is not set
+# CONFIG_SATA_QSTOR is not set
+# CONFIG_SATA_RCAR is not set
+# CONFIG_SATA_SIL is not set
+# CONFIG_SATA_SIL24 is not set
+# CONFIG_SATA_SIS is not set
+# CONFIG_SATA_SVW is not set
+# CONFIG_SATA_SX4 is not set
+# CONFIG_SATA_ULI is not set
+# CONFIG_SATA_VIA is not set
+# CONFIG_SATA_VITESSE is not set
+# CONFIG_SBC_FITPC2_WATCHDOG is not set
+CONFIG_SBITMAP=y
+# CONFIG_SC92031 is not set
+# CONFIG_SCA3000 is not set
+# CONFIG_SCA3300 is not set
+# CONFIG_SCACHE_DEBUGFS is not set
+# CONFIG_SCC is not set
+# CONFIG_SCD30_CORE is not set
+# CONFIG_SCD4X is not set
+# CONFIG_SCF_TORTURE_TEST is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_SCHED_AUTOGROUP is not set
+# CONFIG_SCHED_CLUSTER is not set
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCHED_MC is not set
+CONFIG_SCHED_OMIT_FRAME_POINTER=y
+# CONFIG_SCHED_SMT is not set
+CONFIG_SCHED_STACK_END_CHECK=y
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_3W_SAS is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC94XX is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_SCSI_BFA_FC is not set
+# CONFIG_SCSI_BNX2X_FCOE is not set
+# CONFIG_SCSI_BNX2_ISCSI is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CHELSIO_FCOE is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_CXGB3_ISCSI is not set
+# CONFIG_SCSI_CXGB4_ISCSI is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_ESAS2R is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_FDOMAIN_PCI is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_HISI_SAS is not set
+# CONFIG_SCSI_HPSA is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_ISCI is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_LOGGING is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_SCSI_LPFC is not set
+CONFIG_SCSI_MOD=y
+# CONFIG_SCSI_MPI3MR is not set
+# CONFIG_SCSI_MPT2SAS is not set
+# CONFIG_SCSI_MPT3SAS is not set
+# CONFIG_SCSI_MVSAS is not set
+# CONFIG_SCSI_MVSAS_DEBUG is not set
+# CONFIG_SCSI_MVUMI is not set
+# CONFIG_SCSI_MYRB is not set
+# CONFIG_SCSI_MYRS is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_PM8001 is not set
+# CONFIG_SCSI_PMCRAID is not set
+CONFIG_SCSI_PROC_FS=y
+# CONFIG_SCSI_QLA_FC is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SAS_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+# CONFIG_SCSI_SMARTPQI is not set
+# CONFIG_SCSI_SNIC is not set
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_STEX is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_UFSHCD is not set
+# CONFIG_SCSI_VIRTIO is not set
+# CONFIG_SCSI_WD719X is not set
+# CONFIG_SC_CAMCC_7180 is not set
+# CONFIG_SC_DISPCC_7280 is not set
+# CONFIG_SC_GCC_7280 is not set
+# CONFIG_SC_GCC_8180X is not set
+# CONFIG_SC_GPUCC_7280 is not set
+# CONFIG_SC_GPUCC_8280XP is not set
+# CONFIG_SC_VIDEOCC_7280 is not set
+# CONFIG_SCx200_ACB is not set
+# CONFIG_SDIO_UART is not set
+# CONFIG_SDM_GPUCC_660 is not set
+# CONFIG_SDM_MMCC_660 is not set
+# CONFIG_SDR_MAX2175 is not set
+# CONFIG_SDR_PLATFORM_DRIVERS is not set
+# CONFIG_SDX_GCC_55 is not set
+# CONFIG_SD_ADC_MODULATOR is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_SECCOMP_CACHE_DEBUG is not set
+# CONFIG_SECRETMEM is not set
+CONFIG_SECTION_MISMATCH_WARN_ONLY=y
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_APPARMOR is not set
+CONFIG_SECURITY_DMESG_RESTRICT=y
+# CONFIG_SECURITY_LANDLOCK is not set
+# CONFIG_SECURITY_LOADPIN is not set
+# CONFIG_SECURITY_LOCKDOWN_LSM is not set
+# CONFIG_SECURITY_NETWORK_XFRM is not set
+# CONFIG_SECURITY_PATH is not set
+# CONFIG_SECURITY_SAFESETID is not set
+# CONFIG_SECURITY_SELINUX_AVC_STATS is not set
+# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set
+# CONFIG_SECURITY_SELINUX_DEBUG is not set
+# CONFIG_SECURITY_SELINUX_DEVELOP is not set
+# CONFIG_SECURITY_SMACK is not set
+# CONFIG_SECURITY_TOMOYO is not set
+# CONFIG_SECURITY_YAMA is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_SENSEAIR_SUNRISE_CO2 is not set
+# CONFIG_SENSIRION_SGP30 is not set
+# CONFIG_SENSIRION_SGP40 is not set
+# CONFIG_SENSORS_ABITUGURU is not set
+# CONFIG_SENSORS_ABITUGURU3 is not set
+# CONFIG_SENSORS_ACBEL_FSG032 is not set
+# CONFIG_SENSORS_ACPI_POWER is not set
+# CONFIG_SENSORS_AD7314 is not set
+# CONFIG_SENSORS_AD7414 is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADC128D818 is not set
+# CONFIG_SENSORS_ADCXX is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM1177 is not set
+# CONFIG_SENSORS_ADM1266 is not set
+# CONFIG_SENSORS_ADM1275 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_ADS7871 is not set
+# CONFIG_SENSORS_ADT7310 is not set
+# CONFIG_SENSORS_ADT7410 is not set
+# CONFIG_SENSORS_ADT7411 is not set
+# CONFIG_SENSORS_ADT7462 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+# CONFIG_SENSORS_ADT7475 is not set
+# CONFIG_SENSORS_AHT10 is not set
+# CONFIG_SENSORS_AMC6821 is not set
+# CONFIG_SENSORS_APDS990X is not set
+# CONFIG_SENSORS_APPLESMC is not set
+# CONFIG_SENSORS_AQUACOMPUTER_D5NEXT is not set
+# CONFIG_SENSORS_AS370 is not set
+# CONFIG_SENSORS_ASB100 is not set
+# CONFIG_SENSORS_ASC7621 is not set
+# CONFIG_SENSORS_ASPEED is not set
+# CONFIG_SENSORS_ATK0110 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_AXI_FAN_CONTROL is not set
+# CONFIG_SENSORS_BEL_PFE is not set
+# CONFIG_SENSORS_BH1770 is not set
+# CONFIG_SENSORS_BPA_RS600 is not set
+# CONFIG_SENSORS_CORETEMP is not set
+# CONFIG_SENSORS_CORSAIR_CPRO is not set
+# CONFIG_SENSORS_CORSAIR_PSU is not set
+# CONFIG_SENSORS_DELL_SMM is not set
+# CONFIG_SENSORS_DELTA_AHE50DC_FAN is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_DPS920AB is not set
+# CONFIG_SENSORS_DRIVETEMP is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_DS620 is not set
+# CONFIG_SENSORS_EMC1403 is not set
+# CONFIG_SENSORS_EMC2103 is not set
+# CONFIG_SENSORS_EMC2305 is not set
+# CONFIG_SENSORS_EMC6W201 is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_FAM15H_POWER is not set
+# CONFIG_SENSORS_FSCHMD is not set
+# CONFIG_SENSORS_FSP_3Y is not set
+# CONFIG_SENSORS_FTSTEUTATES is not set
+# CONFIG_SENSORS_G760A is not set
+# CONFIG_SENSORS_G762 is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_GPIO_FAN is not set
+# CONFIG_SENSORS_GSC is not set
+# CONFIG_SENSORS_HDAPS is not set
+# CONFIG_SENSORS_HIH6130 is not set
+# CONFIG_SENSORS_HMC5843 is not set
+# CONFIG_SENSORS_HMC5843_I2C is not set
+# CONFIG_SENSORS_HMC5843_SPI is not set
+# CONFIG_SENSORS_HS3001 is not set
+# CONFIG_SENSORS_I5500 is not set
+# CONFIG_SENSORS_I5K_AMB is not set
+# CONFIG_SENSORS_IBM_CFFPS is not set
+# CONFIG_SENSORS_IIO_HWMON is not set
+# CONFIG_SENSORS_INA209 is not set
+# CONFIG_SENSORS_INA238 is not set
+# CONFIG_SENSORS_INA2XX is not set
+# CONFIG_SENSORS_INA3221 is not set
+# CONFIG_SENSORS_INSPUR_IPSPS is not set
+# CONFIG_SENSORS_IR35221 is not set
+# CONFIG_SENSORS_IR36021 is not set
+# CONFIG_SENSORS_IR38064 is not set
+# CONFIG_SENSORS_IRPS5401 is not set
+# CONFIG_SENSORS_ISL29018 is not set
+# CONFIG_SENSORS_ISL29028 is not set
+# CONFIG_SENSORS_ISL68137 is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_JC42 is not set
+# CONFIG_SENSORS_K10TEMP is not set
+# CONFIG_SENSORS_K8TEMP is not set
+# CONFIG_SENSORS_LINEAGE is not set
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSORS_LM25066 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_LM73 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_LM95234 is not set
+# CONFIG_SENSORS_LM95241 is not set
+# CONFIG_SENSORS_LM95245 is not set
+# CONFIG_SENSORS_LT7182S is not set
+# CONFIG_SENSORS_LTC2945 is not set
+# CONFIG_SENSORS_LTC2947_I2C is not set
+# CONFIG_SENSORS_LTC2947_SPI is not set
+# CONFIG_SENSORS_LTC2978 is not set
+# CONFIG_SENSORS_LTC2990 is not set
+# CONFIG_SENSORS_LTC2992 is not set
+# CONFIG_SENSORS_LTC3815 is not set
+# CONFIG_SENSORS_LTC4151 is not set
+# CONFIG_SENSORS_LTC4215 is not set
+# CONFIG_SENSORS_LTC4222 is not set
+# CONFIG_SENSORS_LTC4245 is not set
+# CONFIG_SENSORS_LTC4260 is not set
+# CONFIG_SENSORS_LTC4261 is not set
+# CONFIG_SENSORS_LTQ_CPUTEMP is not set
+# CONFIG_SENSORS_MAX1111 is not set
+# CONFIG_SENSORS_MAX127 is not set
+# CONFIG_SENSORS_MAX15301 is not set
+# CONFIG_SENSORS_MAX16064 is not set
+# CONFIG_SENSORS_MAX16065 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX16601 is not set
+# CONFIG_SENSORS_MAX1668 is not set
+# CONFIG_SENSORS_MAX197 is not set
+# CONFIG_SENSORS_MAX20730 is not set
+# CONFIG_SENSORS_MAX20751 is not set
+# CONFIG_SENSORS_MAX31722 is not set
+# CONFIG_SENSORS_MAX31730 is not set
+# CONFIG_SENSORS_MAX31760 is not set
+# CONFIG_SENSORS_MAX31785 is not set
+# CONFIG_SENSORS_MAX31790 is not set
+# CONFIG_SENSORS_MAX34440 is not set
+# CONFIG_SENSORS_MAX6620 is not set
+# CONFIG_SENSORS_MAX6621 is not set
+# CONFIG_SENSORS_MAX6639 is not set
+# CONFIG_SENSORS_MAX6642 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_MAX6697 is not set
+# CONFIG_SENSORS_MAX8688 is not set
+# CONFIG_SENSORS_MC34VR500 is not set
+# CONFIG_SENSORS_MCP3021 is not set
+# CONFIG_SENSORS_MP2888 is not set
+# CONFIG_SENSORS_MP2975 is not set
+# CONFIG_SENSORS_MP5023 is not set
+# CONFIG_SENSORS_MPQ7932 is not set
+# CONFIG_SENSORS_MR75203 is not set
+# CONFIG_SENSORS_NCT6683 is not set
+# CONFIG_SENSORS_NCT6775 is not set
+# CONFIG_SENSORS_NCT6775_I2C is not set
+# CONFIG_SENSORS_NCT7802 is not set
+# CONFIG_SENSORS_NCT7904 is not set
+# CONFIG_SENSORS_NPCM7XX is not set
+# CONFIG_SENSORS_NSA320 is not set
+# CONFIG_SENSORS_NTC_THERMISTOR is not set
+# CONFIG_SENSORS_NZXT_KRAKEN2 is not set
+# CONFIG_SENSORS_NZXT_SMART2 is not set
+# CONFIG_SENSORS_OCC_P8_I2C is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_PIM4328 is not set
+# CONFIG_SENSORS_PLI1209BC is not set
+# CONFIG_SENSORS_PM6764TR is not set
+# CONFIG_SENSORS_PMBUS is not set
+# CONFIG_SENSORS_POWR1220 is not set
+# CONFIG_SENSORS_PWM_FAN is not set
+# CONFIG_SENSORS_PXE1610 is not set
+# CONFIG_SENSORS_Q54SJ108A2 is not set
+# CONFIG_SENSORS_RM3100_I2C is not set
+# CONFIG_SENSORS_RM3100_SPI is not set
+# CONFIG_SENSORS_SBRMI is not set
+# CONFIG_SENSORS_SBTSI is not set
+# CONFIG_SENSORS_SCH5627 is not set
+# CONFIG_SENSORS_SCH5636 is not set
+# CONFIG_SENSORS_SCH56XX_COMMON is not set
+# CONFIG_SENSORS_SHT15 is not set
+# CONFIG_SENSORS_SHT21 is not set
+# CONFIG_SENSORS_SHT3x is not set
+# CONFIG_SENSORS_SHT4x is not set
+# CONFIG_SENSORS_SHTC1 is not set
+# CONFIG_SENSORS_SIS5595 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_STPDDC60 is not set
+# CONFIG_SENSORS_STTS751 is not set
+# CONFIG_SENSORS_TC654 is not set
+# CONFIG_SENSORS_TC74 is not set
+# CONFIG_SENSORS_TDA38640 is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_TMP102 is not set
+# CONFIG_SENSORS_TMP103 is not set
+# CONFIG_SENSORS_TMP108 is not set
+# CONFIG_SENSORS_TMP401 is not set
+# CONFIG_SENSORS_TMP421 is not set
+# CONFIG_SENSORS_TMP464 is not set
+# CONFIG_SENSORS_TMP513 is not set
+# CONFIG_SENSORS_TPS23861 is not set
+# CONFIG_SENSORS_TPS40422 is not set
+# CONFIG_SENSORS_TPS53679 is not set
+# CONFIG_SENSORS_TPS546D24 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_SENSORS_TSL2563 is not set
+# CONFIG_SENSORS_UCD9000 is not set
+# CONFIG_SENSORS_UCD9200 is not set
+# CONFIG_SENSORS_VEXPRESS is not set
+# CONFIG_SENSORS_VIA686A is not set
+# CONFIG_SENSORS_VIA_CPUTEMP is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_VT8231 is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83773G is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83795 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_XDPE122 is not set
+# CONFIG_SENSORS_XDPE152 is not set
+# CONFIG_SENSORS_XGENE is not set
+# CONFIG_SENSORS_ZL6100 is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_16550A_VARIANTS=y
+# CONFIG_SERIAL_8250_ACCENT is not set
+# CONFIG_SERIAL_8250_ASPEED_VUART is not set
+# CONFIG_SERIAL_8250_BOCA is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_CS is not set
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+CONFIG_SERIAL_8250_DMA=y
+# CONFIG_SERIAL_8250_DW is not set
+# CONFIG_SERIAL_8250_EM is not set
+# CONFIG_SERIAL_8250_EXAR is not set
+# CONFIG_SERIAL_8250_EXAR_ST16C554 is not set
+# CONFIG_SERIAL_8250_EXTENDED is not set
+# CONFIG_SERIAL_8250_FINTEK is not set
+# CONFIG_SERIAL_8250_FOURPORT is not set
+# CONFIG_SERIAL_8250_HUB6 is not set
+# CONFIG_SERIAL_8250_INGENIC is not set
+# CONFIG_SERIAL_8250_LPSS is not set
+# CONFIG_SERIAL_8250_MANY_PORTS is not set
+# CONFIG_SERIAL_8250_MID is not set
+CONFIG_SERIAL_8250_NR_UARTS=2
+# CONFIG_SERIAL_8250_PCI is not set
+# CONFIG_SERIAL_8250_PCI1XXXX is not set
+# CONFIG_SERIAL_8250_PERICOM is not set
+# CONFIG_SERIAL_8250_RSA is not set
+# CONFIG_SERIAL_8250_RT288X is not set
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_AMBA_PL010 is not set
+# CONFIG_SERIAL_AMBA_PL011 is not set
+# CONFIG_SERIAL_ARC is not set
+# CONFIG_SERIAL_BCM63XX is not set
+# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_DEV_BUS is not set
+CONFIG_SERIAL_EARLYCON=y
+# CONFIG_SERIAL_EARLYCON_SEMIHOST is not set
+# CONFIG_SERIAL_FSL_LINFLEXUART is not set
+# CONFIG_SERIAL_FSL_LPUART is not set
+# CONFIG_SERIAL_GRLIB_GAISLER_APBUART is not set
+# CONFIG_SERIAL_JSM is not set
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX310X is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_SERIAL_OF_PLATFORM is not set
+# CONFIG_SERIAL_PCH_UART is not set
+# CONFIG_SERIAL_RP2 is not set
+# CONFIG_SERIAL_SC16IS7XX is not set
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_SH_SCI is not set
+# CONFIG_SERIAL_SIFIVE is not set
+# CONFIG_SERIAL_SPRD is not set
+# CONFIG_SERIAL_STM32 is not set
+# CONFIG_SERIAL_ST_ASC is not set
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_UARTLITE is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_SERIO is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_AMBAKMI is not set
+# CONFIG_SERIO_APBPS2 is not set
+# CONFIG_SERIO_ARC_PS2 is not set
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_GPIO_PS2 is not set
+# CONFIG_SERIO_I8042 is not set
+# CONFIG_SERIO_LIBPS2 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_SERIO_SUN4I_PS2 is not set
+# CONFIG_SFC is not set
+# CONFIG_SFC_FALCON is not set
+# CONFIG_SFC_SIENA is not set
+# CONFIG_SFP is not set
+# CONFIG_SF_PDMA is not set
+# CONFIG_SGETMASK_SYSCALL is not set
+# CONFIG_SGI_IP22 is not set
+# CONFIG_SGI_IP27 is not set
+# CONFIG_SGI_IP28 is not set
+# CONFIG_SGI_IP30 is not set
+# CONFIG_SGI_IP32 is not set
+# CONFIG_SGI_MFD_IOC3 is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SG_POOL is not set
+# CONFIG_SG_SPLIT is not set
+# CONFIG_SHADOW_CALL_STACK is not set
+CONFIG_SHMEM=y
+# CONFIG_SHRINKER_DEBUG is not set
+# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set
+# CONFIG_SH_ETH is not set
+# CONFIG_SH_TIMER_CMT is not set
+# CONFIG_SH_TIMER_MTU2 is not set
+# CONFIG_SH_TIMER_TMU is not set
+# CONFIG_SI1133 is not set
+# CONFIG_SI1145 is not set
+# CONFIG_SI7005 is not set
+# CONFIG_SI7020 is not set
+# CONFIG_SIBYTE_BIGSUR is not set
+# CONFIG_SIBYTE_CRHONE is not set
+# CONFIG_SIBYTE_LITTLESUR is not set
+# CONFIG_SIBYTE_RHONE is not set
+# CONFIG_SIBYTE_SENTOSA is not set
+# CONFIG_SIBYTE_SWARM is not set
+CONFIG_SIGNALFD=y
+# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set
+# CONFIG_SIOX is not set
+# CONFIG_SIS190 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_SKGE is not set
+# CONFIG_SKY2 is not set
+# CONFIG_SKY2_DEBUG is not set
+# CONFIG_SLAB_DEPRECATED is not set
+CONFIG_SLAB_FREELIST_HARDENED=y
+CONFIG_SLAB_FREELIST_RANDOM=y
+CONFIG_SLAB_MERGE_DEFAULT=y
+# CONFIG_SLHC is not set
+# CONFIG_SLICOSS is not set
+# CONFIG_SLIMBUS is not set
+# CONFIG_SLIP is not set
+CONFIG_SLUB=y
+CONFIG_SLUB_CPU_PARTIAL=y
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+# CONFIG_SLUB_TINY is not set
+# CONFIG_SMARTJOYPLUS_FF is not set
+# CONFIG_SMB_SERVER is not set
+# CONFIG_SMC9194 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_SMP is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_SMSC9420 is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_SMS_SDIO_DRV is not set
+# CONFIG_SMS_USB_DRV is not set
+# CONFIG_SM_CAMCC_8250 is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_SM_GCC_6115 is not set
+# CONFIG_SM_GCC_6125 is not set
+# CONFIG_SM_GCC_6350 is not set
+# CONFIG_SM_GCC_6375 is not set
+# CONFIG_SM_GCC_8350 is not set
+# CONFIG_SND is not set
+# CONFIG_SND_AC97_POWER_SAVE is not set
+# CONFIG_SND_AD1816A is not set
+# CONFIG_SND_AD1848 is not set
+# CONFIG_SND_AD1889 is not set
+# CONFIG_SND_ADLIB is not set
+# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ALOOP is not set
+# CONFIG_SND_ALS100 is not set
+# CONFIG_SND_ALS300 is not set
+# CONFIG_SND_ALS4000 is not set
+# CONFIG_SND_AMD_ACP_CONFIG is not set
+# CONFIG_SND_ARM is not set
+# CONFIG_SND_ASIHPI is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_ATIIXP_MODEM is not set
+# CONFIG_SND_ATMEL_AC97C is not set
+# CONFIG_SND_ATMEL_SOC is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
+# CONFIG_SND_AUDIO_GRAPH_CARD is not set
+# CONFIG_SND_AUDIO_GRAPH_CARD2 is not set
+# CONFIG_SND_AW2 is not set
+# CONFIG_SND_AZT2320 is not set
+# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BCD2000 is not set
+# CONFIG_SND_BCM2835 is not set
+# CONFIG_SND_BCM63XX_I2S_WHISTLER is not set
+# CONFIG_SND_BT87X is not set
+# CONFIG_SND_CA0106 is not set
+# CONFIG_SND_CMI8330 is not set
+# CONFIG_SND_CMIPCI is not set
+# CONFIG_SND_CS4231 is not set
+# CONFIG_SND_CS4236 is not set
+# CONFIG_SND_CS4281 is not set
+# CONFIG_SND_CS46XX is not set
+# CONFIG_SND_CS5530 is not set
+# CONFIG_SND_CS5535AUDIO is not set
+# CONFIG_SND_CTL_FAST_LOOKUP is not set
+# CONFIG_SND_CTL_INPUT_VALIDATION is not set
+# CONFIG_SND_CTXFI is not set
+# CONFIG_SND_DARLA20 is not set
+# CONFIG_SND_DARLA24 is not set
+# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_DESIGNWARE_I2S is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+# CONFIG_SND_ECHO3G is not set
+# CONFIG_SND_EMU10K1 is not set
+# CONFIG_SND_EMU10K1X is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+# CONFIG_SND_ENS1370 is not set
+# CONFIG_SND_ENS1371 is not set
+# CONFIG_SND_ES1688 is not set
+# CONFIG_SND_ES18XX is not set
+# CONFIG_SND_ES1938 is not set
+# CONFIG_SND_ES1968 is not set
+# CONFIG_SND_FIREWIRE is not set
+# CONFIG_SND_FM801 is not set
+# CONFIG_SND_GINA20 is not set
+# CONFIG_SND_GINA24 is not set
+# CONFIG_SND_GUSCLASSIC is not set
+# CONFIG_SND_GUSEXTREME is not set
+# CONFIG_SND_GUSMAX is not set
+# CONFIG_SND_HDA_CODEC_CS8409 is not set
+# CONFIG_SND_HDA_INTEL is not set
+# CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM is not set
+CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0
+CONFIG_SND_HDA_PREALLOC_SIZE=64
+# CONFIG_SND_HDSP is not set
+# CONFIG_SND_HDSPM is not set
+# CONFIG_SND_HRTIMER is not set
+# CONFIG_SND_HWDEP is not set
+# CONFIG_SND_I2S_HI6210_I2S is not set
+# CONFIG_SND_ICE1712 is not set
+# CONFIG_SND_ICE1724 is not set
+# CONFIG_SND_INDIGO is not set
+# CONFIG_SND_INDIGODJ is not set
+# CONFIG_SND_INDIGODJX is not set
+# CONFIG_SND_INDIGOIO is not set
+# CONFIG_SND_INDIGOIOX is not set
+# CONFIG_SND_INTEL8X0 is not set
+# CONFIG_SND_INTEL8X0M is not set
+# CONFIG_SND_INTERWAVE is not set
+# CONFIG_SND_INTERWAVE_STB is not set
+# CONFIG_SND_ISA is not set
+# CONFIG_SND_JZ4740_SOC_I2S is not set
+# CONFIG_SND_KIRKWOOD_SOC is not set
+# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_LAYLA20 is not set
+# CONFIG_SND_LAYLA24 is not set
+# CONFIG_SND_LOLA is not set
+# CONFIG_SND_LX6464ES is not set
+# CONFIG_SND_MAESTRO3 is not set
+CONFIG_SND_MAX_CARDS=16
+# CONFIG_SND_MIA is not set
+# CONFIG_SND_MIPS is not set
+# CONFIG_SND_MIRO is not set
+# CONFIG_SND_MIXART is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_MONA is not set
+# CONFIG_SND_MPC52xx_SOC_EFIKA is not set
+# CONFIG_SND_MPU401 is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_MTS64 is not set
+# CONFIG_SND_MXS_SOC is not set
+# CONFIG_SND_NM256 is not set
+# CONFIG_SND_OPL3SA2 is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_OPTI92X_AD1848 is not set
+# CONFIG_SND_OPTI92X_CS4231 is not set
+# CONFIG_SND_OPTI93X is not set
+CONFIG_SND_OSSEMUL=y
+# CONFIG_SND_OXYGEN is not set
+CONFIG_SND_PCI=y
+# CONFIG_SND_PCM is not set
+# CONFIG_SND_PCMCIA is not set
+# CONFIG_SND_PCMTEST is not set
+# CONFIG_SND_PCM_OSS is not set
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_PCM_TIMER is not set
+# CONFIG_SND_PCM_XRUN_DEBUG is not set
+# CONFIG_SND_PCXHR is not set
+# CONFIG_SND_PDAUDIOCF is not set
+# CONFIG_SND_PORTMAN2X4 is not set
+# CONFIG_SND_POWERPC_SOC is not set
+# CONFIG_SND_PPC is not set
+CONFIG_SND_PROC_FS=y
+# CONFIG_SND_RAWMIDI is not set
+# CONFIG_SND_RIPTIDE is not set
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_SB16 is not set
+# CONFIG_SND_SB8 is not set
+# CONFIG_SND_SBAWE is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_SE6X is not set
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_SEQ_UMP is not set
+# CONFIG_SND_SERIAL_GENERIC is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_SIMPLE_CARD is not set
+# CONFIG_SND_SIS7019 is not set
+# CONFIG_SND_SOC is not set
+# CONFIG_SND_SOC_AC97_CODEC is not set
+# CONFIG_SND_SOC_AD193X_I2C is not set
+# CONFIG_SND_SOC_AD193X_SPI is not set
+# CONFIG_SND_SOC_ADAU1372_I2C is not set
+# CONFIG_SND_SOC_ADAU1372_SPI is not set
+# CONFIG_SND_SOC_ADAU1701 is not set
+# CONFIG_SND_SOC_ADAU1761_I2C is not set
+# CONFIG_SND_SOC_ADAU1761_SPI is not set
+# CONFIG_SND_SOC_ADAU7002 is not set
+# CONFIG_SND_SOC_ADAU7118_HW is not set
+# CONFIG_SND_SOC_ADAU7118_I2C is not set
+# CONFIG_SND_SOC_ADI is not set
+# CONFIG_SND_SOC_AK4104 is not set
+# CONFIG_SND_SOC_AK4118 is not set
+# CONFIG_SND_SOC_AK4375 is not set
+# CONFIG_SND_SOC_AK4458 is not set
+# CONFIG_SND_SOC_AK4554 is not set
+# CONFIG_SND_SOC_AK4613 is not set
+# CONFIG_SND_SOC_AK4642 is not set
+# CONFIG_SND_SOC_AK5386 is not set
+# CONFIG_SND_SOC_AK5558 is not set
+# CONFIG_SND_SOC_ALC5623 is not set
+# CONFIG_SND_SOC_AMD_ACP is not set
+# CONFIG_SND_SOC_AMD_ACP3x is not set
+# CONFIG_SND_SOC_AMD_ACP5x is not set
+# CONFIG_SND_SOC_AMD_RENOIR is not set
+# CONFIG_SND_SOC_AU1XAUDIO is not set
+# CONFIG_SND_SOC_AU1XPSC is not set
+# CONFIG_SND_SOC_AUDIO_IIO_AUX is not set
+# CONFIG_SND_SOC_AW8738 is not set
+# CONFIG_SND_SOC_AW88261 is not set
+# CONFIG_SND_SOC_AW88395 is not set
+# CONFIG_SND_SOC_BD28623 is not set
+# CONFIG_SND_SOC_BT_SCO is not set
+# CONFIG_SND_SOC_CHV3_CODEC is not set
+# CONFIG_SND_SOC_CHV3_I2S is not set
+# CONFIG_SND_SOC_CS35L32 is not set
+# CONFIG_SND_SOC_CS35L33 is not set
+# CONFIG_SND_SOC_CS35L34 is not set
+# CONFIG_SND_SOC_CS35L35 is not set
+# CONFIG_SND_SOC_CS35L36 is not set
+# CONFIG_SND_SOC_CS35L41_I2C is not set
+# CONFIG_SND_SOC_CS35L41_SPI is not set
+# CONFIG_SND_SOC_CS35L45_I2C is not set
+# CONFIG_SND_SOC_CS35L45_SPI is not set
+# CONFIG_SND_SOC_CS35L56_I2C is not set
+# CONFIG_SND_SOC_CS35L56_SPI is not set
+# CONFIG_SND_SOC_CS4234 is not set
+# CONFIG_SND_SOC_CS4265 is not set
+# CONFIG_SND_SOC_CS4270 is not set
+# CONFIG_SND_SOC_CS4271 is not set
+# CONFIG_SND_SOC_CS4271_I2C is not set
+# CONFIG_SND_SOC_CS4271_SPI is not set
+# CONFIG_SND_SOC_CS42L42 is not set
+# CONFIG_SND_SOC_CS42L51_I2C is not set
+# CONFIG_SND_SOC_CS42L52 is not set
+# CONFIG_SND_SOC_CS42L56 is not set
+# CONFIG_SND_SOC_CS42L73 is not set
+# CONFIG_SND_SOC_CS42L83 is not set
+# CONFIG_SND_SOC_CS42XX8_I2C is not set
+# CONFIG_SND_SOC_CS43130 is not set
+# CONFIG_SND_SOC_CS4341 is not set
+# CONFIG_SND_SOC_CS4349 is not set
+# CONFIG_SND_SOC_CS53L30 is not set
+# CONFIG_SND_SOC_CX2072X is not set
+# CONFIG_SND_SOC_DA7213 is not set
+# CONFIG_SND_SOC_DMIC is not set
+# CONFIG_SND_SOC_ES7134 is not set
+# CONFIG_SND_SOC_ES7241 is not set
+# CONFIG_SND_SOC_ES8316 is not set
+# CONFIG_SND_SOC_ES8326 is not set
+# CONFIG_SND_SOC_ES8328 is not set
+# CONFIG_SND_SOC_ES8328_I2C is not set
+# CONFIG_SND_SOC_ES8328_SPI is not set
+# CONFIG_SND_SOC_EUKREA_TLV320 is not set
+# CONFIG_SND_SOC_FSL_ASOC_CARD is not set
+# CONFIG_SND_SOC_FSL_ASRC is not set
+# CONFIG_SND_SOC_FSL_AUD2HTX is not set
+# CONFIG_SND_SOC_FSL_AUDMIX is not set
+# CONFIG_SND_SOC_FSL_ESAI is not set
+# CONFIG_SND_SOC_FSL_MICFIL is not set
+# CONFIG_SND_SOC_FSL_RPMSG is not set
+# CONFIG_SND_SOC_FSL_SAI is not set
+# CONFIG_SND_SOC_FSL_SPDIF is not set
+# CONFIG_SND_SOC_FSL_SSI is not set
+# CONFIG_SND_SOC_FSL_XCVR is not set
+# CONFIG_SND_SOC_GTM601 is not set
+# CONFIG_SND_SOC_HDA is not set
+# CONFIG_SND_SOC_ICS43432 is not set
+# CONFIG_SND_SOC_IDT821034 is not set
+# CONFIG_SND_SOC_IMG is not set
+# CONFIG_SND_SOC_IMX_AUDMIX is not set
+# CONFIG_SND_SOC_IMX_AUDMUX is not set
+# CONFIG_SND_SOC_IMX_CARD is not set
+# CONFIG_SND_SOC_IMX_ES8328 is not set
+# CONFIG_SND_SOC_IMX_HDMI is not set
+# CONFIG_SND_SOC_IMX_RPMSG is not set
+# CONFIG_SND_SOC_IMX_SPDIF is not set
+# CONFIG_SND_SOC_INNO_RK3036 is not set
+# CONFIG_SND_SOC_INTEL_APL is not set
+# CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH is not set
+# CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH is not set
+# CONFIG_SND_SOC_INTEL_BXT_RT298_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH is not set
+# CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH is not set
+# CONFIG_SND_SOC_INTEL_CATPT is not set
+# CONFIG_SND_SOC_INTEL_CFL is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH is not set
+# CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH is not set
+# CONFIG_SND_SOC_INTEL_CML_H is not set
+# CONFIG_SND_SOC_INTEL_CML_LP is not set
+# CONFIG_SND_SOC_INTEL_CNL is not set
+# CONFIG_SND_SOC_INTEL_GLK is not set
+# CONFIG_SND_SOC_INTEL_HASWELL is not set
+# CONFIG_SND_SOC_INTEL_KBL is not set
+# CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH is not set
+# CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH is not set
+# CONFIG_SND_SOC_INTEL_KEEMBAY is not set
+# CONFIG_SND_SOC_INTEL_SKL is not set
+# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH is not set
+# CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH is not set
+# CONFIG_SND_SOC_INTEL_SKL_RT286_MACH is not set
+# CONFIG_SND_SOC_INTEL_SKYLAKE is not set
+# CONFIG_SND_SOC_INTEL_SST is not set
+CONFIG_SND_SOC_INTEL_SST_TOPLEVEL=y
+# CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES is not set
+# CONFIG_SND_SOC_JZ4725B_CODEC is not set
+# CONFIG_SND_SOC_JZ4740_CODEC is not set
+# CONFIG_SND_SOC_JZ4770_CODEC is not set
+# CONFIG_SND_SOC_LOONGSON_CARD is not set
+# CONFIG_SND_SOC_LOONGSON_I2S_PCI is not set
+# CONFIG_SND_SOC_LPASS_RX_MACRO is not set
+# CONFIG_SND_SOC_LPASS_TX_MACRO is not set
+# CONFIG_SND_SOC_LPASS_VA_MACRO is not set
+# CONFIG_SND_SOC_LPASS_WSA_MACRO is not set
+# CONFIG_SND_SOC_MAX9759 is not set
+# CONFIG_SND_SOC_MAX98088 is not set
+# CONFIG_SND_SOC_MAX98090 is not set
+# CONFIG_SND_SOC_MAX98357A is not set
+# CONFIG_SND_SOC_MAX98373 is not set
+# CONFIG_SND_SOC_MAX98373_I2C is not set
+# CONFIG_SND_SOC_MAX98388 is not set
+# CONFIG_SND_SOC_MAX98390 is not set
+# CONFIG_SND_SOC_MAX98396 is not set
+# CONFIG_SND_SOC_MAX98504 is not set
+# CONFIG_SND_SOC_MAX98520 is not set
+# CONFIG_SND_SOC_MAX9860 is not set
+# CONFIG_SND_SOC_MAX9867 is not set
+# CONFIG_SND_SOC_MAX98927 is not set
+# CONFIG_SND_SOC_MEDIATEK is not set
+# CONFIG_SND_SOC_MPC5200_AC97 is not set
+# CONFIG_SND_SOC_MPC5200_I2S is not set
+# CONFIG_SND_SOC_MSM8916_WCD_ANALOG is not set
+# CONFIG_SND_SOC_MSM8916_WCD_DIGITAL is not set
+# CONFIG_SND_SOC_MT2701 is not set
+# CONFIG_SND_SOC_MT6351 is not set
+# CONFIG_SND_SOC_MT6358 is not set
+# CONFIG_SND_SOC_MT6359 is not set
+# CONFIG_SND_SOC_MT6359_ACCDET is not set
+# CONFIG_SND_SOC_MT6660 is not set
+# CONFIG_SND_SOC_MT6797 is not set
+# CONFIG_SND_SOC_MT8173 is not set
+# CONFIG_SND_SOC_MT8183 is not set
+# CONFIG_SND_SOC_MT8186 is not set
+# CONFIG_SND_SOC_MT8188 is not set
+# CONFIG_SND_SOC_MT8192 is not set
+# CONFIG_SND_SOC_MT8195 is not set
+# CONFIG_SND_SOC_MTK_BTCVSD is not set
+# CONFIG_SND_SOC_NAU8315 is not set
+# CONFIG_SND_SOC_NAU8540 is not set
+# CONFIG_SND_SOC_NAU8810 is not set
+# CONFIG_SND_SOC_NAU8821 is not set
+# CONFIG_SND_SOC_NAU8822 is not set
+# CONFIG_SND_SOC_NAU8824 is not set
+# CONFIG_SND_SOC_PCM1681 is not set
+# CONFIG_SND_SOC_PCM1789_I2C is not set
+# CONFIG_SND_SOC_PCM179X_I2C is not set
+# CONFIG_SND_SOC_PCM179X_SPI is not set
+# CONFIG_SND_SOC_PCM186X_I2C is not set
+# CONFIG_SND_SOC_PCM186X_SPI is not set
+# CONFIG_SND_SOC_PCM3060_I2C is not set
+# CONFIG_SND_SOC_PCM3060_SPI is not set
+# CONFIG_SND_SOC_PCM3168A_I2C is not set
+# CONFIG_SND_SOC_PCM3168A_SPI is not set
+# CONFIG_SND_SOC_PCM5102A is not set
+# CONFIG_SND_SOC_PCM512x_I2C is not set
+# CONFIG_SND_SOC_PCM512x_SPI is not set
+# CONFIG_SND_SOC_PEB2466 is not set
+# CONFIG_SND_SOC_QCOM is not set
+# CONFIG_SND_SOC_RK3328 is not set
+# CONFIG_SND_SOC_RK817 is not set
+# CONFIG_SND_SOC_ROCKCHIP is not set
+# CONFIG_SND_SOC_RT5616 is not set
+# CONFIG_SND_SOC_RT5631 is not set
+# CONFIG_SND_SOC_RT5640 is not set
+# CONFIG_SND_SOC_RT5659 is not set
+# CONFIG_SND_SOC_RT5677_SPI is not set
+# CONFIG_SND_SOC_RT9120 is not set
+# CONFIG_SND_SOC_SGTL5000 is not set
+# CONFIG_SND_SOC_SIMPLE_AMPLIFIER is not set
+# CONFIG_SND_SOC_SIMPLE_MUX is not set
+# CONFIG_SND_SOC_SMA1303 is not set
+# CONFIG_SND_SOC_SOF_TOPLEVEL is not set
+# CONFIG_SND_SOC_SPDIF is not set
+# CONFIG_SND_SOC_SRC4XXX_I2C is not set
+# CONFIG_SND_SOC_SSM2305 is not set
+# CONFIG_SND_SOC_SSM2518 is not set
+# CONFIG_SND_SOC_SSM2602_I2C is not set
+# CONFIG_SND_SOC_SSM2602_SPI is not set
+# CONFIG_SND_SOC_SSM3515 is not set
+# CONFIG_SND_SOC_SSM4567 is not set
+# CONFIG_SND_SOC_STA32X is not set
+# CONFIG_SND_SOC_STA350 is not set
+# CONFIG_SND_SOC_STI_SAS is not set
+# CONFIG_SND_SOC_TAS2552 is not set
+# CONFIG_SND_SOC_TAS2562 is not set
+# CONFIG_SND_SOC_TAS2764 is not set
+# CONFIG_SND_SOC_TAS2770 is not set
+# CONFIG_SND_SOC_TAS2780 is not set
+# CONFIG_SND_SOC_TAS2781_I2C is not set
+# CONFIG_SND_SOC_TAS5086 is not set
+# CONFIG_SND_SOC_TAS571X is not set
+# CONFIG_SND_SOC_TAS5720 is not set
+# CONFIG_SND_SOC_TAS5805M is not set
+# CONFIG_SND_SOC_TAS6424 is not set
+# CONFIG_SND_SOC_TDA7419 is not set
+# CONFIG_SND_SOC_TFA9879 is not set
+# CONFIG_SND_SOC_TFA989X is not set
+# CONFIG_SND_SOC_TLV320ADC3XXX is not set
+# CONFIG_SND_SOC_TLV320ADCX140 is not set
+# CONFIG_SND_SOC_TLV320AIC23_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC23_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC31XX is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC3X is not set
+# CONFIG_SND_SOC_TLV320AIC3X_I2C is not set
+# CONFIG_SND_SOC_TLV320AIC3X_SPI is not set
+# CONFIG_SND_SOC_TPA6130A2 is not set
+# CONFIG_SND_SOC_TS3A227E is not set
+# CONFIG_SND_SOC_TSCS42XX is not set
+# CONFIG_SND_SOC_TSCS454 is not set
+# CONFIG_SND_SOC_UDA1334 is not set
+# CONFIG_SND_SOC_WM8510 is not set
+# CONFIG_SND_SOC_WM8523 is not set
+# CONFIG_SND_SOC_WM8524 is not set
+# CONFIG_SND_SOC_WM8580 is not set
+# CONFIG_SND_SOC_WM8711 is not set
+# CONFIG_SND_SOC_WM8728 is not set
+# CONFIG_SND_SOC_WM8731 is not set
+# CONFIG_SND_SOC_WM8731_I2C is not set
+# CONFIG_SND_SOC_WM8731_SPI is not set
+# CONFIG_SND_SOC_WM8737 is not set
+# CONFIG_SND_SOC_WM8741 is not set
+# CONFIG_SND_SOC_WM8750 is not set
+# CONFIG_SND_SOC_WM8753 is not set
+# CONFIG_SND_SOC_WM8770 is not set
+# CONFIG_SND_SOC_WM8776 is not set
+# CONFIG_SND_SOC_WM8782 is not set
+# CONFIG_SND_SOC_WM8804_I2C is not set
+# CONFIG_SND_SOC_WM8804_SPI is not set
+# CONFIG_SND_SOC_WM8903 is not set
+# CONFIG_SND_SOC_WM8904 is not set
+# CONFIG_SND_SOC_WM8940 is not set
+# CONFIG_SND_SOC_WM8960 is not set
+# CONFIG_SND_SOC_WM8961 is not set
+# CONFIG_SND_SOC_WM8962 is not set
+# CONFIG_SND_SOC_WM8974 is not set
+# CONFIG_SND_SOC_WM8978 is not set
+# CONFIG_SND_SOC_WM8985 is not set
+# CONFIG_SND_SOC_XILINX_AUDIO_FORMATTER is not set
+# CONFIG_SND_SOC_XILINX_I2S is not set
+# CONFIG_SND_SOC_XILINX_SPDIF is not set
+# CONFIG_SND_SOC_XTFPGA_I2S is not set
+# CONFIG_SND_SOC_ZL38060 is not set
+# CONFIG_SND_SONICVIBES is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SND_SSCAPE is not set
+# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI is not set
+# CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI is not set
+# CONFIG_SND_SUN4I_CODEC is not set
+# CONFIG_SND_SUPPORT_OLD_API is not set
+# CONFIG_SND_TEST_COMPONENT is not set
+# CONFIG_SND_TIMER is not set
+# CONFIG_SND_TRIDENT is not set
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_6FIRE is not set
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_AUDIO_MIDI_V2 is not set
+# CONFIG_SND_USB_CAIAQ is not set
+# CONFIG_SND_USB_HIFACE is not set
+# CONFIG_SND_USB_POD is not set
+# CONFIG_SND_USB_PODHD is not set
+# CONFIG_SND_USB_TONEPORT is not set
+# CONFIG_SND_USB_UA101 is not set
+# CONFIG_SND_USB_US122L is not set
+# CONFIG_SND_USB_USX2Y is not set
+# CONFIG_SND_USB_VARIAX is not set
+# CONFIG_SND_VERBOSE_PRINTK is not set
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VIA82XX is not set
+# CONFIG_SND_VIA82XX_MODEM is not set
+# CONFIG_SND_VIRTIO is not set
+# CONFIG_SND_VIRTUOSO is not set
+# CONFIG_SND_VX222 is not set
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_WAVEFRONT is not set
+CONFIG_SND_X86=y
+# CONFIG_SND_XEN_FRONTEND is not set
+# CONFIG_SND_YMFPCI is not set
+# CONFIG_SNI_RM is not set
+# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set
+# CONFIG_SOCK_CGROUP_DATA is not set
+# CONFIG_SOC_AM33XX is not set
+# CONFIG_SOC_AM43XX is not set
+# CONFIG_SOC_BRCMSTB is not set
+# CONFIG_SOC_DRA7XX is not set
+# CONFIG_SOC_HAS_OMAP2_SDRC is not set
+# CONFIG_SOC_OMAP5 is not set
+# CONFIG_SOC_TI is not set
+# CONFIG_SOFTLOCKUP_DETECTOR is not set
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_SONYPI is not set
+# CONFIG_SONY_LAPTOP is not set
+# CONFIG_SOUND is not set
+# CONFIG_SOUNDWIRE is not set
+# CONFIG_SOUND_OSS_CORE is not set
+# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set
+# CONFIG_SP5100_TCO is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+# CONFIG_SPARSE_IRQ is not set
+# CONFIG_SPEAKUP is not set
+# CONFIG_SPI is not set
+# CONFIG_SPINLOCK_TEST is not set
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_AMD is not set
+# CONFIG_SPI_AU1550 is not set
+# CONFIG_SPI_AX88796C is not set
+# CONFIG_SPI_AXI_SPI_ENGINE is not set
+# CONFIG_SPI_BCM2835 is not set
+# CONFIG_SPI_BCM63XX_HSSPI is not set
+# CONFIG_SPI_BCMBCA_HSSPI is not set
+# CONFIG_SPI_BCM_QSPI is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_BUTTERFLY is not set
+# CONFIG_SPI_CADENCE is not set
+# CONFIG_SPI_CADENCE_QUADSPI is not set
+# CONFIG_SPI_CADENCE_XSPI is not set
+# CONFIG_SPI_DEBUG is not set
+# CONFIG_SPI_DESIGNWARE is not set
+# CONFIG_SPI_FSL_DSPI is not set
+# CONFIG_SPI_FSL_ESPI is not set
+# CONFIG_SPI_FSL_SPI is not set
+# CONFIG_SPI_GPIO is not set
+# CONFIG_SPI_IMG_SPFI is not set
+# CONFIG_SPI_LANTIQ_SSC is not set
+# CONFIG_SPI_LM70_LLP is not set
+# CONFIG_SPI_LOOPBACK_TEST is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_SPI_MEM is not set
+# CONFIG_SPI_MICROCHIP_CORE is not set
+# CONFIG_SPI_MICROCHIP_CORE_QSPI is not set
+# CONFIG_SPI_MPC52xx is not set
+# CONFIG_SPI_MPC52xx_PSC is not set
+# CONFIG_SPI_MUX is not set
+# CONFIG_SPI_MXIC is not set
+# CONFIG_SPI_NXP_FLEXSPI is not set
+# CONFIG_SPI_OCTEON is not set
+# CONFIG_SPI_OC_TINY is not set
+# CONFIG_SPI_ORION is not set
+# CONFIG_SPI_PCI1XXXX is not set
+# CONFIG_SPI_PL022 is not set
+# CONFIG_SPI_PPC4xx is not set
+# CONFIG_SPI_PXA2XX is not set
+# CONFIG_SPI_PXA2XX_PCI is not set
+# CONFIG_SPI_QCOM_QSPI is not set
+# CONFIG_SPI_ROCKCHIP is not set
+# CONFIG_SPI_S3C64XX is not set
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_SIFIVE is not set
+# CONFIG_SPI_SLAVE is not set
+# CONFIG_SPI_SN_F_OSPI is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_THUNDERX is not set
+# CONFIG_SPI_TI_QSPI is not set
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_SPI_TOPCLIFF_PCH is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_ZYNQMP_GQSPI is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_SPMI is not set
+# CONFIG_SPS30 is not set
+# CONFIG_SPS30_I2C is not set
+# CONFIG_SPS30_SERIAL is not set
+CONFIG_SQUASHFS=y
+# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set
+# CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT is not set
+# CONFIG_SQUASHFS_COMPILE_DECOMP_MULTI is not set
+CONFIG_SQUASHFS_COMPILE_DECOMP_MULTI_PERCPU=y
+# CONFIG_SQUASHFS_COMPILE_DECOMP_SINGLE is not set
+CONFIG_SQUASHFS_EMBEDDED=y
+# CONFIG_SQUASHFS_FILE_CACHE is not set
+CONFIG_SQUASHFS_FILE_DIRECT=y
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_SQUASHFS_LZ4 is not set
+# CONFIG_SQUASHFS_LZO is not set
+# CONFIG_SQUASHFS_XATTR is not set
+CONFIG_SQUASHFS_XZ=y
+# CONFIG_SQUASHFS_ZLIB is not set
+# CONFIG_SQUASHFS_ZSTD is not set
+# CONFIG_SRAM is not set
+# CONFIG_SRF04 is not set
+# CONFIG_SRF08 is not set
+# CONFIG_SSB is not set
+# CONFIG_SSB_DRIVER_GPIO is not set
+# CONFIG_SSB_HOST_SOC is not set
+# CONFIG_SSB_PCMCIAHOST is not set
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB_SDIOHOST is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SSIF_IPMI_BMC is not set
+# CONFIG_STACKPROTECTOR is not set
+# CONFIG_STACKPROTECTOR_PER_TASK is not set
+# CONFIG_STACKPROTECTOR_STRONG is not set
+# CONFIG_STACKTRACE is not set
+# CONFIG_STACKTRACE_BUILD_ID is not set
+CONFIG_STACKTRACE_SUPPORT=y
+# CONFIG_STACK_TRACER is not set
+# CONFIG_STACK_VALIDATION is not set
+CONFIG_STAGING=y
+# CONFIG_STAGING_BOARD is not set
+# CONFIG_STAGING_MEDIA is not set
+CONFIG_STANDALONE=y
+# CONFIG_STATIC_KEYS_SELFTEST is not set
+# CONFIG_STATIC_USERMODEHELPER is not set
+# CONFIG_STE10XP is not set
+# CONFIG_STK3310 is not set
+# CONFIG_STK8312 is not set
+# CONFIG_STK8BA50 is not set
+# CONFIG_STM is not set
+# CONFIG_STMMAC_ETH is not set
+# CONFIG_STMMAC_PCI is not set
+# CONFIG_STMMAC_PLATFORM is not set
+# CONFIG_STMMAC_SELFTESTS is not set
+# CONFIG_STMPE_ADC is not set
+# CONFIG_STM_DUMMY is not set
+# CONFIG_STM_SOURCE_CONSOLE is not set
+CONFIG_STP=y
+# CONFIG_STREAM_PARSER is not set
+# CONFIG_STRICT_DEVMEM is not set
+CONFIG_STRICT_KERNEL_RWX=y
+CONFIG_STRICT_MODULE_RWX=y
+# CONFIG_STRING_SELFTEST is not set
+CONFIG_STRIP_ASM_SYMS=y
+# CONFIG_STX104 is not set
+# CONFIG_ST_UVIS25 is not set
+# CONFIG_SUN4I_GPADC is not set
+# CONFIG_SUN50I_DE2_BUS is not set
+# CONFIG_SUN50I_ERRATUM_UNKNOWN1 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_SUNRPC is not set
+# CONFIG_SUNRPC_DEBUG is not set
+# CONFIG_SUNRPC_GSS is not set
+# CONFIG_SUNXI_SRAM is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_SURFACE_PLATFORMS is not set
+# CONFIG_SUSPEND is not set
+# CONFIG_SUSPEND_SKIP_SYNC is not set
+CONFIG_SWAP=y
+# CONFIG_SWCONFIG is not set
+# CONFIG_SWCONFIG_B53 is not set
+# CONFIG_SWCONFIG_B53_MMAP_DRIVER is not set
+# CONFIG_SWCONFIG_B53_SPI_DRIVER is not set
+# CONFIG_SWCONFIG_B53_SRAB_DRIVER is not set
+# CONFIG_SWCONFIG_LEDS is not set
+# CONFIG_SWIOTLB is not set
+# CONFIG_SWIOTLB_DYNAMIC is not set
+# CONFIG_SW_SYNC is not set
+# CONFIG_SX9310 is not set
+# CONFIG_SX9324 is not set
+# CONFIG_SX9360 is not set
+# CONFIG_SX9500 is not set
+# CONFIG_SXGBE_ETH is not set
+CONFIG_SYMBOLIC_ERRNAME=y
+# CONFIG_SYNC_FILE is not set
+# CONFIG_SYNTH_EVENTS is not set
+# CONFIG_SYNTH_EVENT_GEN_TEST is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_SYSCON_REBOOT_MODE is not set
+CONFIG_SYSCTL=y
+CONFIG_SYSFS=y
+# CONFIG_SYSFS_SYSCALL is not set
+# CONFIG_SYSTEMPORT is not set
+# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set
+# CONFIG_SYSTEM_DATA_VERIFICATION is not set
+# CONFIG_SYSTEM_TRUSTED_KEYRING is not set
+CONFIG_SYSTEM_TRUSTED_KEYS=""
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_T5403 is not set
+# CONFIG_TARGET_CORE is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_TASKS_RCU is not set
+# CONFIG_TASK_XACCT is not set
+# CONFIG_TC35815 is not set
+# CONFIG_TCG_ATMEL is not set
+# CONFIG_TCG_CRB is not set
+# CONFIG_TCG_FTPM_TEE is not set
+# CONFIG_TCG_INFINEON is not set
+# CONFIG_TCG_NSC is not set
+# CONFIG_TCG_TIS is not set
+# CONFIG_TCG_TIS_I2C is not set
+# CONFIG_TCG_TIS_I2C_ATMEL is not set
+# CONFIG_TCG_TIS_I2C_CR50 is not set
+# CONFIG_TCG_TIS_I2C_INFINEON is not set
+# CONFIG_TCG_TIS_I2C_NUVOTON is not set
+# CONFIG_TCG_TIS_SPI is not set
+# CONFIG_TCG_TIS_ST33ZP24_I2C is not set
+# CONFIG_TCG_TIS_ST33ZP24_SPI is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_TCG_VTPM_PROXY is not set
+# CONFIG_TCG_XEN is not set
+# CONFIG_TCIC is not set
+CONFIG_TCP_CONG_ADVANCED=y
+# CONFIG_TCP_CONG_BBR is not set
+# CONFIG_TCP_CONG_BIC is not set
+# CONFIG_TCP_CONG_CDG is not set
+CONFIG_TCP_CONG_CUBIC=y
+# CONFIG_TCP_CONG_DCTCP is not set
+# CONFIG_TCP_CONG_HSTCP is not set
+# CONFIG_TCP_CONG_HTCP is not set
+# CONFIG_TCP_CONG_HYBLA is not set
+# CONFIG_TCP_CONG_ILLINOIS is not set
+# CONFIG_TCP_CONG_LP is not set
+# CONFIG_TCP_CONG_NV is not set
+# CONFIG_TCP_CONG_SCALABLE is not set
+# CONFIG_TCP_CONG_VEGAS is not set
+# CONFIG_TCP_CONG_VENO is not set
+# CONFIG_TCP_CONG_WESTWOOD is not set
+# CONFIG_TCP_CONG_YEAH is not set
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_TCS3414 is not set
+# CONFIG_TCS3472 is not set
+# CONFIG_TEE is not set
+# CONFIG_TEGRA_AHB is not set
+# CONFIG_TEGRA_HOST1X is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_TERANETICS_PHY is not set
+# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
+# CONFIG_TEST_BITMAP is not set
+# CONFIG_TEST_BITOPS is not set
+# CONFIG_TEST_BLACKHOLE_DEV is not set
+# CONFIG_TEST_BPF is not set
+# CONFIG_TEST_CLOCKSOURCE_WATCHDOG is not set
+# CONFIG_TEST_DEBUG_VIRTUAL is not set
+# CONFIG_TEST_DHRY is not set
+# CONFIG_TEST_DIV64 is not set
+# CONFIG_TEST_DYNAMIC_DEBUG is not set
+# CONFIG_TEST_FIRMWARE is not set
+# CONFIG_TEST_FREE_PAGES is not set
+# CONFIG_TEST_HEXDUMP is not set
+# CONFIG_TEST_IDA is not set
+# CONFIG_TEST_KMOD is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_TEST_LKM is not set
+# CONFIG_TEST_LOCKUP is not set
+# CONFIG_TEST_MAPLE_TREE is not set
+# CONFIG_TEST_MEMCAT_P is not set
+# CONFIG_TEST_MEMINIT is not set
+# CONFIG_TEST_MIN_HEAP is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_TEST_PRINTF is not set
+# CONFIG_TEST_REF_TRACKER is not set
+# CONFIG_TEST_RHASHTABLE is not set
+# CONFIG_TEST_SCANF is not set
+# CONFIG_TEST_SORT is not set
+# CONFIG_TEST_STATIC_KEYS is not set
+# CONFIG_TEST_STRING_HELPERS is not set
+# CONFIG_TEST_SYSCTL is not set
+# CONFIG_TEST_UBSAN is not set
+# CONFIG_TEST_UDELAY is not set
+# CONFIG_TEST_USER_COPY is not set
+# CONFIG_TEST_UUID is not set
+# CONFIG_TEST_VMALLOC is not set
+# CONFIG_TEST_XARRAY is not set
+CONFIG_TEXTSEARCH=y
+# CONFIG_TEXTSEARCH_BM is not set
+# CONFIG_TEXTSEARCH_FSM is not set
+# CONFIG_TEXTSEARCH_KMP is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_DEFAULT_GOV_BANG_BANG is not set
+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
+# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_EMULATION is not set
+# CONFIG_THERMAL_GOV_BANG_BANG is not set
+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
+# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set
+# CONFIG_THERMAL_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_THERMAL_MMIO is not set
+# CONFIG_THERMAL_NETLINK is not set
+# CONFIG_THERMAL_STATISTICS is not set
+# CONFIG_THERMAL_WRITABLE_TRIPS is not set
+# CONFIG_THINKPAD_ACPI is not set
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_THUMB2_KERNEL is not set
+# CONFIG_THUNDER_NIC_BGX is not set
+# CONFIG_THUNDER_NIC_PF is not set
+# CONFIG_THUNDER_NIC_RGX is not set
+# CONFIG_THUNDER_NIC_VF is not set
+# CONFIG_TICK_CPU_ACCOUNTING is not set
+CONFIG_TICK_ONESHOT=y
+# CONFIG_TIFM_CORE is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_TIMERFD=y
+# CONFIG_TIMERLAT_TRACER is not set
+# CONFIG_TIME_NS is not set
+# CONFIG_TINYDRM_HX8357D is not set
+# CONFIG_TINYDRM_ILI9163 is not set
+# CONFIG_TINYDRM_ILI9225 is not set
+# CONFIG_TINYDRM_ILI9341 is not set
+# CONFIG_TINYDRM_ILI9486 is not set
+# CONFIG_TINYDRM_MI0283QT is not set
+# CONFIG_TINYDRM_REPAPER is not set
+# CONFIG_TINYDRM_ST7586 is not set
+# CONFIG_TINYDRM_ST7735R is not set
+CONFIG_TINY_RCU=y
+# CONFIG_TIPC is not set
+# CONFIG_TI_ADC081C is not set
+# CONFIG_TI_ADC0832 is not set
+# CONFIG_TI_ADC084S021 is not set
+# CONFIG_TI_ADC108S102 is not set
+# CONFIG_TI_ADC12138 is not set
+# CONFIG_TI_ADC128S052 is not set
+# CONFIG_TI_ADC161S626 is not set
+# CONFIG_TI_ADS1015 is not set
+# CONFIG_TI_ADS1100 is not set
+# CONFIG_TI_ADS124S08 is not set
+# CONFIG_TI_ADS131E08 is not set
+# CONFIG_TI_ADS7924 is not set
+# CONFIG_TI_ADS7950 is not set
+# CONFIG_TI_ADS8344 is not set
+# CONFIG_TI_ADS8688 is not set
+# CONFIG_TI_AM335X_ADC is not set
+# CONFIG_TI_CPSW is not set
+# CONFIG_TI_CPSW_PHY_SEL is not set
+# CONFIG_TI_CPTS is not set
+# CONFIG_TI_DAC082S085 is not set
+# CONFIG_TI_DAC5571 is not set
+# CONFIG_TI_DAC7311 is not set
+# CONFIG_TI_DAC7612 is not set
+# CONFIG_TI_DAVINCI_MDIO is not set
+# CONFIG_TI_LMP92064 is not set
+# CONFIG_TI_ST is not set
+# CONFIG_TI_TLC4541 is not set
+# CONFIG_TI_TMAG5273 is not set
+# CONFIG_TI_TSC2046 is not set
+# CONFIG_TLAN is not set
+# CONFIG_TLS is not set
+# CONFIG_TLS_DEVICE is not set
+# CONFIG_TLS_TOE is not set
+# CONFIG_TMD_HERMES is not set
+# CONFIG_TMP006 is not set
+# CONFIG_TMP007 is not set
+# CONFIG_TMP117 is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_INODE64 is not set
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_TMPFS_QUOTA is not set
+CONFIG_TMPFS_XATTR=y
+# CONFIG_TOPSTAR_LAPTOP is not set
+# CONFIG_TORTURE_TEST is not set
+# CONFIG_TOSHIBA_HAPS is not set
+# CONFIG_TOUCHSCREEN_88PM860X is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_AD7879_I2C is not set
+# CONFIG_TOUCHSCREEN_AD7879_SPI is not set
+# CONFIG_TOUCHSCREEN_ADC is not set
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AR1021_I2C is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 is not set
+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_BU21029 is not set
+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set
+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8505 is not set
+# CONFIG_TOUCHSCREEN_COLIBRI_VF50 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_I2C is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_SPI is not set
+# CONFIG_TOUCHSCREEN_CYTTSP5 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_SPI is not set
+# CONFIG_TOUCHSCREEN_DA9034 is not set
+# CONFIG_TOUCHSCREEN_DA9052 is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_EGALAX is not set
+# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set
+# CONFIG_TOUCHSCREEN_EKTF2127 is not set
+# CONFIG_TOUCHSCREEN_ELAN is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_EXC3000 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GOODIX is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_HIDEEP is not set
+# CONFIG_TOUCHSCREEN_HIMAX_HX83112B is not set
+# CONFIG_TOUCHSCREEN_HP600 is not set
+# CONFIG_TOUCHSCREEN_HP7XX is not set
+# CONFIG_TOUCHSCREEN_HTCPEN is not set
+# CONFIG_TOUCHSCREEN_HYCON_HY46XX is not set
+# CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX is not set
+# CONFIG_TOUCHSCREEN_ILI210X is not set
+# CONFIG_TOUCHSCREEN_ILITEK is not set
+# CONFIG_TOUCHSCREEN_IMAGIS is not set
+# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_IPAQ_MICRO is not set
+# CONFIG_TOUCHSCREEN_IPROC is not set
+# CONFIG_TOUCHSCREEN_IQS5XX is not set
+# CONFIG_TOUCHSCREEN_IQS7211 is not set
+# CONFIG_TOUCHSCREEN_LPC32XX is not set
+# CONFIG_TOUCHSCREEN_MAX11801 is not set
+# CONFIG_TOUCHSCREEN_MC13783 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set
+# CONFIG_TOUCHSCREEN_MIGOR is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_MMS114 is not set
+# CONFIG_TOUCHSCREEN_MSG2638 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MX25 is not set
+# CONFIG_TOUCHSCREEN_MXS_LRADC is not set
+# CONFIG_TOUCHSCREEN_NOVATEK_NVT_TS is not set
+# CONFIG_TOUCHSCREEN_PCAP is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_RASPBERRYPI_FW is not set
+# CONFIG_TOUCHSCREEN_RM_TS is not set
+# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set
+# CONFIG_TOUCHSCREEN_S6SY761 is not set
+# CONFIG_TOUCHSCREEN_SILEAD is not set
+# CONFIG_TOUCHSCREEN_SIS_I2C is not set
+# CONFIG_TOUCHSCREEN_ST1232 is not set
+# CONFIG_TOUCHSCREEN_STMFTS is not set
+# CONFIG_TOUCHSCREEN_STMPE is not set
+# CONFIG_TOUCHSCREEN_SUN4I is not set
+# CONFIG_TOUCHSCREEN_SUR40 is not set
+# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set
+# CONFIG_TOUCHSCREEN_SX8654 is not set
+# CONFIG_TOUCHSCREEN_TI_AM335X_TSC is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+# CONFIG_TOUCHSCREEN_TS4800 is not set
+# CONFIG_TOUCHSCREEN_TSC2004 is not set
+# CONFIG_TOUCHSCREEN_TSC2005 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_TSC2007_IIO is not set
+# CONFIG_TOUCHSCREEN_TSC200X_CORE is not set
+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
+# CONFIG_TOUCHSCREEN_USB_3M is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_USB_DMC_TSC10 is not set
+# CONFIG_TOUCHSCREEN_USB_E2I is not set
+# CONFIG_TOUCHSCREEN_USB_EASYTOUCH is not set
+# CONFIG_TOUCHSCREEN_USB_EGALAX is not set
+# CONFIG_TOUCHSCREEN_USB_ELO is not set
+# CONFIG_TOUCHSCREEN_USB_ETT_TC45USB is not set
+# CONFIG_TOUCHSCREEN_USB_ETURBO is not set
+# CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH is not set
+# CONFIG_TOUCHSCREEN_USB_GOTOP is not set
+# CONFIG_TOUCHSCREEN_USB_GUNZE is not set
+# CONFIG_TOUCHSCREEN_USB_IDEALTEK is not set
+# CONFIG_TOUCHSCREEN_USB_IRTOUCH is not set
+# CONFIG_TOUCHSCREEN_USB_ITM is not set
+# CONFIG_TOUCHSCREEN_USB_JASTEC is not set
+# CONFIG_TOUCHSCREEN_USB_NEXIO is not set
+# CONFIG_TOUCHSCREEN_USB_PANJIT is not set
+# CONFIG_TOUCHSCREEN_USB_ZYTRONIC is not set
+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set
+# CONFIG_TOUCHSCREEN_WM831X is not set
+# CONFIG_TOUCHSCREEN_WM9705 is not set
+# CONFIG_TOUCHSCREEN_WM9712 is not set
+# CONFIG_TOUCHSCREEN_WM9713 is not set
+# CONFIG_TOUCHSCREEN_WM97XX is not set
+# CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE is not set
+# CONFIG_TOUCHSCREEN_ZET6223 is not set
+# CONFIG_TOUCHSCREEN_ZFORCE is not set
+# CONFIG_TOUCHSCREEN_ZINITIX is not set
+# CONFIG_TPL0102 is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_TRACEPOINT_BENCHMARK is not set
+# CONFIG_TRACER_SNAPSHOT is not set
+# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set
+# CONFIG_TRACE_BRANCH_PROFILING is not set
+# CONFIG_TRACE_EVAL_MAP_FILE is not set
+# CONFIG_TRACE_EVENT_INJECT is not set
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_TRACE_MMIO_ACCESS is not set
+CONFIG_TRACING_SUPPORT=y
+CONFIG_TRAD_SIGNALS=y
+# CONFIG_TRANSPARENT_HUGEPAGE is not set
+# CONFIG_TREE_RCU is not set
+# CONFIG_TRIM_UNUSED_KSYMS is not set
+# CONFIG_TRUSTED_FOUNDATIONS is not set
+# CONFIG_TRUSTED_KEYS is not set
+# CONFIG_TRUSTED_KEYS_CAAM is not set
+# CONFIG_TRUSTED_KEYS_TEE is not set
+# CONFIG_TRUSTED_KEYS_TPM is not set
+# CONFIG_TSL2583 is not set
+# CONFIG_TSL2591 is not set
+# CONFIG_TSL2772 is not set
+# CONFIG_TSL4531 is not set
+# CONFIG_TSNEP is not set
+# CONFIG_TSYS01 is not set
+# CONFIG_TSYS02D is not set
+# CONFIG_TTPCI_EEPROM is not set
+CONFIG_TTY=y
+# CONFIG_TTY_PRINTK is not set
+# CONFIG_TUN is not set
+# CONFIG_TUN_VNET_CROSS_LE is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_TWL4030_MADC is not set
+# CONFIG_TWL6030_GPADC is not set
+# CONFIG_TWL6040_CORE is not set
+# CONFIG_TXGBE is not set
+# CONFIG_TYPEC is not set
+# CONFIG_TYPEC_DP_ALTMODE is not set
+# CONFIG_TYPEC_TCPM is not set
+# CONFIG_TYPEC_UCSI is not set
+# CONFIG_TYPHOON is not set
+# CONFIG_UACCE is not set
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_UBIFS_ATIME_SUPPORT is not set
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+# CONFIG_UBIFS_FS_AUTHENTICATION is not set
+CONFIG_UBIFS_FS_LZO=y
+# CONFIG_UBIFS_FS_SECURITY is not set
+CONFIG_UBIFS_FS_XATTR=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_UBIFS_FS_ZSTD=y
+# CONFIG_UBSAN is not set
+CONFIG_UBSAN_ALIGNMENT=y
+CONFIG_UBSAN_BOOL=y
+# CONFIG_UBSAN_DIV_ZERO is not set
+CONFIG_UBSAN_ENUM=y
+CONFIG_UBSAN_SHIFT=y
+# CONFIG_UBSAN_UNREACHABLE is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDMABUF is not set
+CONFIG_UEVENT_HELPER=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_UFS_FS is not set
+# CONFIG_UHID is not set
+CONFIG_UID16=y
+# CONFIG_UIMAGE_FIT_BLK is not set
+# CONFIG_UIO is not set
+# CONFIG_ULTRA is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_UNICODE is not set
+CONFIG_UNIX=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_UNIX_DIAG is not set
+CONFIG_UNIX_SCM=y
+# CONFIG_UNWINDER_FRAME_POINTER is not set
+# CONFIG_UPROBES is not set
+# CONFIG_UPROBE_EVENTS is not set
+# CONFIG_US5182D is not set
+# CONFIG_USB is not set
+# CONFIG_USB4 is not set
+# CONFIG_USBIP_CORE is not set
+CONFIG_USBIP_VHCI_HC_PORTS=8
+CONFIG_USBIP_VHCI_NR_HCS=1
+# CONFIG_USBIP_VUDC is not set
+# CONFIG_USBPCWATCHDOG is not set
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_AIRSPY is not set
+CONFIG_USB_ALI_M5632=y
+# CONFIG_USB_AMD5536UDC is not set
+CONFIG_USB_AN2720=y
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARMLINUX=y
+# CONFIG_USB_ATM is not set
+# CONFIG_USB_AUDIO is not set
+CONFIG_USB_AUTOSUSPEND_DELAY=2
+# CONFIG_USB_BDC_UDC is not set
+CONFIG_USB_BELKIN=y
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+# CONFIG_USB_CDNS3 is not set
+# CONFIG_USB_CDNS3_IMX is not set
+# CONFIG_USB_CDNS3_PCI_WRAP is not set
+# CONFIG_USB_CDNSP_PCI is not set
+# CONFIG_USB_CDNS_SUPPORT is not set
+# CONFIG_USB_CHAOSKEY is not set
+# CONFIG_USB_CHIPIDEA is not set
+# CONFIG_USB_CHIPIDEA_GENERIC is not set
+# CONFIG_USB_CHIPIDEA_IMX is not set
+# CONFIG_USB_CHIPIDEA_MSM is not set
+# CONFIG_USB_CHIPIDEA_PCI is not set
+# CONFIG_USB_CHIPIDEA_TEGRA is not set
+# CONFIG_USB_CONFIGFS is not set
+# CONFIG_USB_CONN_GPIO is not set
+# CONFIG_USB_CXACRU is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+CONFIG_USB_DEFAULT_PERSIST=y
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_DUMMY_HCD is not set
+# CONFIG_USB_DWC2 is not set
+# CONFIG_USB_DWC2_DEBUG is not set
+# CONFIG_USB_DWC2_DUAL_ROLE is not set
+# CONFIG_USB_DWC2_HOST is not set
+# CONFIG_USB_DWC2_PERIPHERAL is not set
+# CONFIG_USB_DWC2_TRACK_MISSED_SOFS is not set
+# CONFIG_USB_DWC3 is not set
+# CONFIG_USB_DWC3_EXYNOS is not set
+# CONFIG_USB_DWC3_HAPS is not set
+# CONFIG_USB_DWC3_KEYSTONE is not set
+# CONFIG_USB_DWC3_OCTEON is not set
+# CONFIG_USB_DWC3_OF_SIMPLE is not set
+# CONFIG_USB_DWC3_PCI is not set
+# CONFIG_USB_DWC3_QCOM is not set
+# CONFIG_USB_DWC3_ULPI is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_EG20T is not set
+# CONFIG_USB_EHCI_FSL is not set
+# CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_EHCI_HCD_AT91 is not set
+# CONFIG_USB_EHCI_HCD_OMAP is not set
+# CONFIG_USB_EHCI_HCD_PPC_OF is not set
+# CONFIG_USB_EHCI_MV is not set
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_TT_NEWSCHED=y
+# CONFIG_USB_EHSET_TEST_FIXTURE is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EPSON2888 is not set
+# CONFIG_USB_ETH is not set
+# CONFIG_USB_EZUSB_FX2 is not set
+# CONFIG_USB_FEW_INIT_RETRIES is not set
+# CONFIG_USB_FOTG210_HCD is not set
+# CONFIG_USB_FOTG210_UDC is not set
+# CONFIG_USB_FSL_USB2 is not set
+# CONFIG_USB_FUNCTIONFS is not set
+# CONFIG_USB_FUSB300 is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+# CONFIG_USB_GADGET_DEBUG_FS is not set
+CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
+CONFIG_USB_GADGET_VBUS_DRAW=2
+# CONFIG_USB_GADGET_XILINX is not set
+# CONFIG_USB_GL860 is not set
+# CONFIG_USB_GOKU is not set
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_USB_GR_UDC is not set
+# CONFIG_USB_GSPCA is not set
+# CONFIG_USB_GSPCA_BENQ is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_CPIA1 is not set
+# CONFIG_USB_GSPCA_DTCS033 is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_JEILINJ is not set
+# CONFIG_USB_GSPCA_JL2005BCD is not set
+# CONFIG_USB_GSPCA_KINECT is not set
+# CONFIG_USB_GSPCA_KONICA is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_MR97310A is not set
+# CONFIG_USB_GSPCA_NW80X is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_OV534 is not set
+# CONFIG_USB_GSPCA_OV534_9 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7302 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SE401 is not set
+# CONFIG_USB_GSPCA_SN9C2028 is not set
+# CONFIG_USB_GSPCA_SN9C20X is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA1528 is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_SQ905 is not set
+# CONFIG_USB_GSPCA_SQ905C is not set
+# CONFIG_USB_GSPCA_SQ930X is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_STK1135 is not set
+# CONFIG_USB_GSPCA_STV0680 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TOPRO is not set
+# CONFIG_USB_GSPCA_TOUPTEK is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_VICAM is not set
+# CONFIG_USB_GSPCA_XIRLINK_CIT is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_USB_G_ACM_MS is not set
+# CONFIG_USB_G_DBGP is not set
+# CONFIG_USB_G_HID is not set
+# CONFIG_USB_G_MULTI is not set
+# CONFIG_USB_G_NCM is not set
+# CONFIG_USB_G_NOKIA is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_G_WEBCAM is not set
+# CONFIG_USB_HACKRF is not set
+# CONFIG_USB_HCD_TEST_MODE is not set
+# CONFIG_USB_HID is not set
+# CONFIG_USB_HIDDEV is not set
+# CONFIG_USB_HSIC_USB3503 is not set
+# CONFIG_USB_HSIC_USB4604 is not set
+# CONFIG_USB_HSO is not set
+# CONFIG_USB_HUB_USB251XB is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_IPHETH is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1301 is not set
+# CONFIG_USB_ISP1362_HCD is not set
+# CONFIG_USB_ISP1760 is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_KC2190 is not set
+# CONFIG_USB_LAN78XX is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set
+# CONFIG_USB_LED_TRIG is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LGM_PHY is not set
+# CONFIG_USB_LINK_LAYER_TEST is not set
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_M66592 is not set
+# CONFIG_USB_MASS_STORAGE is not set
+# CONFIG_USB_MAX3420_UDC is not set
+# CONFIG_USB_MAX3421_HCD is not set
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_MOUSE is not set
+# CONFIG_USB_MSI2500 is not set
+# CONFIG_USB_MTU3 is not set
+# CONFIG_USB_MUSB_GADGET is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_MUSB_HOST is not set
+# CONFIG_USB_MV_U3D is not set
+# CONFIG_USB_MV_UDC is not set
+# CONFIG_USB_MXS_PHY is not set
+# CONFIG_USB_NET2272 is not set
+# CONFIG_USB_NET2280 is not set
+# CONFIG_USB_NET_AQC111 is not set
+# CONFIG_USB_NET_AX88179_178A is not set
+# CONFIG_USB_NET_AX8817X is not set
+# CONFIG_USB_NET_CDCETHER is not set
+# CONFIG_USB_NET_CDC_EEM is not set
+# CONFIG_USB_NET_CDC_MBIM is not set
+# CONFIG_USB_NET_CDC_NCM is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_CH9200 is not set
+# CONFIG_USB_NET_CX82310_ETH is not set
+# CONFIG_USB_NET_DM9601 is not set
+# CONFIG_USB_NET_DRIVERS is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set
+# CONFIG_USB_NET_INT51X1 is not set
+# CONFIG_USB_NET_KALMIA is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_QMI_WWAN is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_RNDIS_WLAN is not set
+# CONFIG_USB_NET_SMSC75XX is not set
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_SR9700 is not set
+# CONFIG_USB_NET_SR9800 is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_USB_OHCI_HCD is not set
+# CONFIG_USB_OHCI_HCD_PCI is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF_BE is not set
+# CONFIG_USB_OHCI_HCD_PPC_OF_LE is not set
+# CONFIG_USB_OHCI_HCD_SSB is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_ONBOARD_HUB is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set
+# CONFIG_USB_OTG_FSM is not set
+# CONFIG_USB_OTG_PRODUCTLIST is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_PCI is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_PHY is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_PWC_INPUT_EVDEV is not set
+# CONFIG_USB_PXA27X is not set
+# CONFIG_USB_QCOM_EUD is not set
+# CONFIG_USB_R8A66597 is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_RAW_GADGET is not set
+# CONFIG_USB_RENESAS_USBHS is not set
+# CONFIG_USB_ROLES_INTEL_XHCI is not set
+# CONFIG_USB_ROLE_SWITCH is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_RTL8152 is not set
+# CONFIG_USB_RTL8153_ECM is not set
+# CONFIG_USB_S2255 is not set
+# CONFIG_USB_SERIAL is not set
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+# CONFIG_USB_SERIAL_BELKIN is not set
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_CH348 is not set
+# CONFIG_USB_SERIAL_CP210X is not set
+# CONFIG_USB_SERIAL_CYBERJACK is not set
+# CONFIG_USB_SERIAL_CYPRESS_M8 is not set
+# CONFIG_USB_SERIAL_DEBUG is not set
+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT_TI is not set
+# CONFIG_USB_SERIAL_EMPEG is not set
+# CONFIG_USB_SERIAL_F81232 is not set
+# CONFIG_USB_SERIAL_F8153X is not set
+# CONFIG_USB_SERIAL_FTDI_SIO is not set
+# CONFIG_USB_SERIAL_GARMIN is not set
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_IPAQ is not set
+# CONFIG_USB_SERIAL_IPW is not set
+# CONFIG_USB_SERIAL_IR is not set
+# CONFIG_USB_SERIAL_IUU is not set
+# CONFIG_USB_SERIAL_KEYSPAN is not set
+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
+# CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_METRO is not set
+# CONFIG_USB_SERIAL_MOS7715_PARPORT is not set
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_MXUPORT is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+# CONFIG_USB_SERIAL_OMNINET is not set
+# CONFIG_USB_SERIAL_OPTICON is not set
+# CONFIG_USB_SERIAL_OPTION is not set
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_PL2303 is not set
+# CONFIG_USB_SERIAL_QCAUX is not set
+# CONFIG_USB_SERIAL_QT2 is not set
+# CONFIG_USB_SERIAL_QUALCOMM is not set
+# CONFIG_USB_SERIAL_SAFE is not set
+CONFIG_USB_SERIAL_SAFE_PADDED=y
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+# CONFIG_USB_SERIAL_SIMPLE is not set
+# CONFIG_USB_SERIAL_SPCP8X5 is not set
+# CONFIG_USB_SERIAL_SSU100 is not set
+# CONFIG_USB_SERIAL_SYMBOL is not set
+# CONFIG_USB_SERIAL_TI is not set
+# CONFIG_USB_SERIAL_UPD78F0730 is not set
+# CONFIG_USB_SERIAL_VISOR is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+# CONFIG_USB_SERIAL_WISHBONE is not set
+# CONFIG_USB_SERIAL_XR is not set
+# CONFIG_USB_SERIAL_XSENS_MT is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_SIERRA_NET is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_SNP_UDC_PLAT is not set
+# CONFIG_USB_SPEEDTOUCH is not set
+# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_ENE_UB6250 is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_REALTEK is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STV06XX is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_TMC is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_UAS is not set
+# CONFIG_USB_UEAGLEATM is not set
+# CONFIG_USB_ULPI is not set
+# CONFIG_USB_ULPI_BUS is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_USB_USS720 is not set
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
+# CONFIG_USB_VL600 is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_XEN_HCD is not set
+# CONFIG_USB_XHCI_DBGCAP is not set
+# CONFIG_USB_XHCI_HCD is not set
+# CONFIG_USB_XHCI_MVEBU is not set
+# CONFIG_USB_XHCI_PCI_RENESAS is not set
+# CONFIG_USB_XUSBATM is not set
+# CONFIG_USB_YUREX is not set
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_USB_ZERO is not set
+# CONFIG_USELIB is not set
+# CONFIG_USERFAULTFD is not set
+# CONFIG_USERIO is not set
+# CONFIG_USER_DECRYPTED_DATA is not set
+# CONFIG_USER_EVENTS is not set
+# CONFIG_USE_OF is not set
+# CONFIG_UTS_NS is not set
+# CONFIG_U_SERIAL_CONSOLE is not set
+# CONFIG_V4L_MEM2MEM_DRIVERS is not set
+# CONFIG_V4L_PLATFORM_DRIVERS is not set
+# CONFIG_V4L_TEST_DRIVERS is not set
+# CONFIG_VALIDATE_FS_PARSER is not set
+# CONFIG_VBOXGUEST is not set
+# CONFIG_VCAP is not set
+# CONFIG_VCNL3020 is not set
+# CONFIG_VCNL4000 is not set
+# CONFIG_VCNL4035 is not set
+# CONFIG_VCPU_STALL_DETECTOR is not set
+# CONFIG_VDPA is not set
+CONFIG_VDSO=y
+# CONFIG_VEML6030 is not set
+# CONFIG_VEML6070 is not set
+# CONFIG_VETH is not set
+# CONFIG_VEXPRESS_CONFIG is not set
+# CONFIG_VF610_ADC is not set
+# CONFIG_VF610_DAC is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_VFIO is not set
+# CONFIG_VFIO_FSL_MC is not set
+# CONFIG_VFIO_PLATFORM is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VGA_ARB is not set
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_VGA_SWITCHEROO is not set
+# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set
+CONFIG_VHOST_MENU=y
+# CONFIG_VHOST_NET is not set
+# CONFIG_VHOST_VSOCK is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_VELOCITY is not set
+# CONFIG_VIDEO_AD5820 is not set
+# CONFIG_VIDEO_ADP1653 is not set
+# CONFIG_VIDEO_ADV7170 is not set
+# CONFIG_VIDEO_ADV7175 is not set
+# CONFIG_VIDEO_ADV7180 is not set
+# CONFIG_VIDEO_ADV7183 is not set
+# CONFIG_VIDEO_ADV7343 is not set
+# CONFIG_VIDEO_ADV7393 is not set
+# CONFIG_VIDEO_ADV748X is not set
+# CONFIG_VIDEO_ADV7511 is not set
+# CONFIG_VIDEO_ADV7604 is not set
+# CONFIG_VIDEO_ADV7842 is not set
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_AK7375 is not set
+# CONFIG_VIDEO_AK881X is not set
+# CONFIG_VIDEO_AM437X_VPFE is not set
+# CONFIG_VIDEO_AR0521 is not set
+# CONFIG_VIDEO_ASPEED is not set
+# CONFIG_VIDEO_ATMEL_ISC is not set
+# CONFIG_VIDEO_ATMEL_ISI is not set
+# CONFIG_VIDEO_AU0828 is not set
+# CONFIG_VIDEO_BCM2835 is not set
+# CONFIG_VIDEO_BT819 is not set
+# CONFIG_VIDEO_BT848 is not set
+# CONFIG_VIDEO_BT856 is not set
+# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_CADENCE_CSI2RX is not set
+# CONFIG_VIDEO_CADENCE_CSI2TX is not set
+# CONFIG_VIDEO_CAFE_CCIC is not set
+# CONFIG_VIDEO_CAMERA_SENSOR is not set
+# CONFIG_VIDEO_CCS is not set
+# CONFIG_VIDEO_COBALT is not set
+# CONFIG_VIDEO_CODA is not set
+# CONFIG_VIDEO_CS3308 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_CX231XX is not set
+# CONFIG_VIDEO_CX2341X is not set
+# CONFIG_VIDEO_CX25821 is not set
+# CONFIG_VIDEO_CX25840 is not set
+# CONFIG_VIDEO_CX88 is not set
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_DS90UB913 is not set
+# CONFIG_VIDEO_DS90UB953 is not set
+# CONFIG_VIDEO_DS90UB960 is not set
+# CONFIG_VIDEO_DT3155 is not set
+# CONFIG_VIDEO_DW9714 is not set
+# CONFIG_VIDEO_DW9719 is not set
+# CONFIG_VIDEO_DW9768 is not set
+# CONFIG_VIDEO_DW9807_VCM is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_ET8EK8 is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+# CONFIG_VIDEO_GO7007 is not set
+# CONFIG_VIDEO_GS1662 is not set
+# CONFIG_VIDEO_HDPVR is not set
+# CONFIG_VIDEO_HEXIUM_GEMINI is not set
+# CONFIG_VIDEO_HEXIUM_ORION is not set
+# CONFIG_VIDEO_HI556 is not set
+# CONFIG_VIDEO_HI846 is not set
+# CONFIG_VIDEO_HI847 is not set
+# CONFIG_VIDEO_I2C is not set
+# CONFIG_VIDEO_IMX208 is not set
+# CONFIG_VIDEO_IMX214 is not set
+# CONFIG_VIDEO_IMX219 is not set
+# CONFIG_VIDEO_IMX258 is not set
+# CONFIG_VIDEO_IMX274 is not set
+# CONFIG_VIDEO_IMX290 is not set
+# CONFIG_VIDEO_IMX296 is not set
+# CONFIG_VIDEO_IMX319 is not set
+# CONFIG_VIDEO_IMX334 is not set
+# CONFIG_VIDEO_IMX335 is not set
+# CONFIG_VIDEO_IMX355 is not set
+# CONFIG_VIDEO_IMX412 is not set
+# CONFIG_VIDEO_IMX7_CSI is not set
+# CONFIG_VIDEO_IMX8MQ_MIPI_CSI2 is not set
+# CONFIG_VIDEO_IMX8_ISI is not set
+# CONFIG_VIDEO_IMX8_JPEG is not set
+# CONFIG_VIDEO_IMX_MIPI_CSIS is not set
+# CONFIG_VIDEO_IMX_PXP is not set
+# CONFIG_VIDEO_IR_I2C is not set
+# CONFIG_VIDEO_ISL7998X is not set
+# CONFIG_VIDEO_IVTV is not set
+# CONFIG_VIDEO_KS0127 is not set
+# CONFIG_VIDEO_LM3560 is not set
+# CONFIG_VIDEO_LM3646 is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_MAX9286 is not set
+# CONFIG_VIDEO_MEM2MEM_DEINTERLACE is not set
+# CONFIG_VIDEO_ML86V7667 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_MT9M001 is not set
+# CONFIG_VIDEO_MT9M111 is not set
+# CONFIG_VIDEO_MT9P031 is not set
+# CONFIG_VIDEO_MT9T112 is not set
+# CONFIG_VIDEO_MT9V011 is not set
+# CONFIG_VIDEO_MT9V032 is not set
+# CONFIG_VIDEO_MT9V111 is not set
+# CONFIG_VIDEO_MUX is not set
+# CONFIG_VIDEO_MXB is not set
+# CONFIG_VIDEO_OG01A1B is not set
+# CONFIG_VIDEO_OMAP2_VOUT is not set
+# CONFIG_VIDEO_OV02A10 is not set
+# CONFIG_VIDEO_OV08D10 is not set
+# CONFIG_VIDEO_OV13858 is not set
+# CONFIG_VIDEO_OV13B10 is not set
+# CONFIG_VIDEO_OV2640 is not set
+# CONFIG_VIDEO_OV2659 is not set
+# CONFIG_VIDEO_OV2680 is not set
+# CONFIG_VIDEO_OV2685 is not set
+# CONFIG_VIDEO_OV2740 is not set
+# CONFIG_VIDEO_OV5640 is not set
+# CONFIG_VIDEO_OV5645 is not set
+# CONFIG_VIDEO_OV5647 is not set
+# CONFIG_VIDEO_OV5648 is not set
+# CONFIG_VIDEO_OV5670 is not set
+# CONFIG_VIDEO_OV5675 is not set
+# CONFIG_VIDEO_OV5693 is not set
+# CONFIG_VIDEO_OV5695 is not set
+# CONFIG_VIDEO_OV6650 is not set
+# CONFIG_VIDEO_OV7251 is not set
+# CONFIG_VIDEO_OV7640 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_OV772X is not set
+# CONFIG_VIDEO_OV7740 is not set
+# CONFIG_VIDEO_OV8856 is not set
+# CONFIG_VIDEO_OV8865 is not set
+# CONFIG_VIDEO_OV9282 is not set
+# CONFIG_VIDEO_OV9640 is not set
+# CONFIG_VIDEO_OV9650 is not set
+# CONFIG_VIDEO_OV9734 is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_RCAR_CSI2 is not set
+# CONFIG_VIDEO_RCAR_ISP is not set
+# CONFIG_VIDEO_RCAR_VIN is not set
+# CONFIG_VIDEO_RDACM20 is not set
+# CONFIG_VIDEO_RDACM21 is not set
+# CONFIG_VIDEO_RJ54N1 is not set
+# CONFIG_VIDEO_ROCKCHIP_ISP1 is not set
+# CONFIG_VIDEO_S5C73M3 is not set
+# CONFIG_VIDEO_S5K5BAF is not set
+# CONFIG_VIDEO_S5K6A3 is not set
+# CONFIG_VIDEO_SAA6588 is not set
+# CONFIG_VIDEO_SAA6752HS is not set
+# CONFIG_VIDEO_SAA7110 is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA7127 is not set
+# CONFIG_VIDEO_SAA7134 is not set
+# CONFIG_VIDEO_SAA7164 is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_SAA7185 is not set
+# CONFIG_VIDEO_SOLO6X10 is not set
+# CONFIG_VIDEO_SONY_BTF_MPX is not set
+# CONFIG_VIDEO_STK1160 is not set
+# CONFIG_VIDEO_ST_MIPID02 is not set
+# CONFIG_VIDEO_SUN4I_CSI is not set
+# CONFIG_VIDEO_SUN6I_CSI is not set
+# CONFIG_VIDEO_SUN8I_A83T_MIPI_CSI2 is not set
+# CONFIG_VIDEO_TC358743 is not set
+# CONFIG_VIDEO_TC358746 is not set
+# CONFIG_VIDEO_TDA1997X is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_THS7303 is not set
+# CONFIG_VIDEO_THS8200 is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TVP514X is not set
+# CONFIG_VIDEO_TVP5150 is not set
+# CONFIG_VIDEO_TVP7002 is not set
+# CONFIG_VIDEO_TW2804 is not set
+# CONFIG_VIDEO_TW5864 is not set
+# CONFIG_VIDEO_TW68 is not set
+# CONFIG_VIDEO_TW9903 is not set
+# CONFIG_VIDEO_TW9906 is not set
+# CONFIG_VIDEO_TW9910 is not set
+# CONFIG_VIDEO_UDA1342 is not set
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_USBTV is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+# CONFIG_VIDEO_VPX3220 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_XILINX is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_CONSOLE is not set
+# CONFIG_VIRTIO_FS is not set
+# CONFIG_VIRTIO_INPUT is not set
+CONFIG_VIRTIO_MENU=y
+# CONFIG_VIRTIO_MMIO is not set
+# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set
+# CONFIG_VIRTIO_PCI is not set
+# CONFIG_VIRTUALIZATION is not set
+# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
+# CONFIG_VIRT_DRIVERS is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_VL53L0X_I2C is not set
+# CONFIG_VL6180 is not set
+CONFIG_VLAN_8021Q=y
+# CONFIG_VLAN_8021Q_GVRP is not set
+# CONFIG_VLAN_8021Q_MVRP is not set
+# CONFIG_VMAP_STACK is not set
+# CONFIG_VME_BUS is not set
+# CONFIG_VMLINUX_MAP is not set
+# CONFIG_VMSPLIT_1G is not set
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_2G_OPT is not set
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_3G_OPT is not set
+# CONFIG_VMWARE_PVSCSI is not set
+# CONFIG_VMWARE_VMCI is not set
+# CONFIG_VMXNET3 is not set
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_VORTEX is not set
+# CONFIG_VSOCKETS is not set
+# CONFIG_VSOCKETS_DIAG is not set
+# CONFIG_VT is not set
+# CONFIG_VT6655 is not set
+# CONFIG_VT6656 is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_VXLAN is not set
+# CONFIG_VZ89X is not set
+# CONFIG_W1 is not set
+# CONFIG_W1_CON is not set
+# CONFIG_W1_MASTER_DS2482 is not set
+# CONFIG_W1_MASTER_DS2490 is not set
+# CONFIG_W1_MASTER_GPIO is not set
+# CONFIG_W1_MASTER_MATROX is not set
+# CONFIG_W1_MASTER_SGI is not set
+# CONFIG_W1_SLAVE_DS2405 is not set
+# CONFIG_W1_SLAVE_DS2406 is not set
+# CONFIG_W1_SLAVE_DS2408 is not set
+# CONFIG_W1_SLAVE_DS2413 is not set
+# CONFIG_W1_SLAVE_DS2423 is not set
+# CONFIG_W1_SLAVE_DS2430 is not set
+# CONFIG_W1_SLAVE_DS2431 is not set
+# CONFIG_W1_SLAVE_DS2433 is not set
+# CONFIG_W1_SLAVE_DS2438 is not set
+# CONFIG_W1_SLAVE_DS250X is not set
+# CONFIG_W1_SLAVE_DS2780 is not set
+# CONFIG_W1_SLAVE_DS2781 is not set
+# CONFIG_W1_SLAVE_DS2805 is not set
+# CONFIG_W1_SLAVE_DS28E04 is not set
+# CONFIG_W1_SLAVE_DS28E17 is not set
+# CONFIG_W1_SLAVE_SMEM is not set
+# CONFIG_W1_SLAVE_THERM is not set
+# CONFIG_W83627HF_WDT is not set
+# CONFIG_W83877F_WDT is not set
+# CONFIG_W83977F_WDT is not set
+# CONFIG_WAN is not set
+# CONFIG_WANXL is not set
+# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_CORE is not set
+CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y
+# CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT is not set
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+CONFIG_WATCHDOG_OPEN_TIMEOUT=0
+# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set
+# CONFIG_WATCHDOG_SYSFS is not set
+# CONFIG_WATCH_QUEUE is not set
+# CONFIG_WD80x3 is not set
+# CONFIG_WDAT_WDT is not set
+# CONFIG_WDTPCI is not set
+# CONFIG_WERROR is not set
+# CONFIG_WEXT_CORE is not set
+# CONFIG_WEXT_PRIV is not set
+# CONFIG_WEXT_PROC is not set
+# CONFIG_WEXT_SPY is not set
+# CONFIG_WIREGUARD is not set
+CONFIG_WIRELESS=y
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_WIZNET_W5100 is not set
+# CONFIG_WIZNET_W5300 is not set
+# CONFIG_WL1251 is not set
+# CONFIG_WL12XX is not set
+# CONFIG_WL18XX is not set
+CONFIG_WLAN=y
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+# CONFIG_WLAN_VENDOR_ATH is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+# CONFIG_WLAN_VENDOR_CISCO is not set
+# CONFIG_WLAN_VENDOR_INTEL is not set
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+# CONFIG_WLAN_VENDOR_MEDIATEK is not set
+# CONFIG_WLAN_VENDOR_MICROCHIP is not set
+# CONFIG_WLAN_VENDOR_PURELIFI is not set
+# CONFIG_WLAN_VENDOR_QUANTENNA is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+# CONFIG_WLAN_VENDOR_REALTEK is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_SILABS is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
+# CONFIG_WLCORE is not set
+# CONFIG_WPCM450_SOC is not set
+# CONFIG_WQ_CPU_INTENSIVE_REPORT is not set
+CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y
+# CONFIG_WQ_WATCHDOG is not set
+# CONFIG_WWAN is not set
+# CONFIG_WWAN_HWSIM is not set
+# CONFIG_WW_MUTEX_SELFTEST is not set
+# CONFIG_X25 is not set
+# CONFIG_X509_CERTIFICATE_PARSER is not set
+# CONFIG_X86_PKG_TEMP_THERMAL is not set
+# CONFIG_X9250 is not set
+# CONFIG_XDP_SOCKETS is not set
+# CONFIG_XEN is not set
+# CONFIG_XEN_GRANT_DMA_ALLOC is not set
+# CONFIG_XEN_PVCALLS_FRONTEND is not set
+CONFIG_XEN_SCRUB_PAGES_DEFAULT=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_INTERFACE is not set
+# CONFIG_XFRM_IPCOMP is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFS_DEBUG is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_XFS_ONLINE_SCRUB is not set
+# CONFIG_XFS_POSIX_ACL is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_SUPPORT_ASCII_CI is not set
+# CONFIG_XFS_SUPPORT_V4 is not set
+# CONFIG_XFS_WARN is not set
+# CONFIG_XILINX_AXI_EMAC is not set
+# CONFIG_XILINX_DMA is not set
+# CONFIG_XILINX_EMACLITE is not set
+# CONFIG_XILINX_GMII2RGMII is not set
+# CONFIG_XILINX_INTC is not set
+# CONFIG_XILINX_LL_TEMAC is not set
+# CONFIG_XILINX_SDFEC is not set
+# CONFIG_XILINX_VCU is not set
+# CONFIG_XILINX_WATCHDOG is not set
+# CONFIG_XILINX_WINDOW_WATCHDOG is not set
+# CONFIG_XILINX_XADC is not set
+# CONFIG_XILINX_XDMA is not set
+# CONFIG_XILINX_ZYNQMP_DMA is not set
+# CONFIG_XILINX_ZYNQMP_DPDMA is not set
+# CONFIG_XILLYBUS is not set
+# CONFIG_XILLYUSB is not set
+# CONFIG_XIL_AXIS_FIFO is not set
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_XMON is not set
+CONFIG_XZ_DEC=y
+# CONFIG_XZ_DEC_ARM is not set
+# CONFIG_XZ_DEC_ARMTHUMB is not set
+# CONFIG_XZ_DEC_BCJ is not set
+# CONFIG_XZ_DEC_IA64 is not set
+# CONFIG_XZ_DEC_MICROLZMA is not set
+# CONFIG_XZ_DEC_POWERPC is not set
+# CONFIG_XZ_DEC_SPARC is not set
+# CONFIG_XZ_DEC_TEST is not set
+# CONFIG_XZ_DEC_X86 is not set
+# CONFIG_YAM is not set
+# CONFIG_YAMAHA_YAS530 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_YENTA is not set
+# CONFIG_YENTA_O2 is not set
+# CONFIG_YENTA_RICOH is not set
+# CONFIG_YENTA_TI is not set
+# CONFIG_YENTA_TOSHIBA is not set
+# CONFIG_ZBUD is not set
+# CONFIG_ZD1211RW is not set
+# CONFIG_ZD1211RW_DEBUG is not set
+# CONFIG_ZEROPLUS_FF is not set
+# CONFIG_ZERO_CALL_USED_REGS is not set
+# CONFIG_ZIIRAVE_WATCHDOG is not set
+# CONFIG_ZISOFS is not set
+# CONFIG_ZLIB_DEFLATE is not set
+# CONFIG_ZLIB_INFLATE is not set
+CONFIG_ZONE_DMA=y
+# CONFIG_ZOPT2201 is not set
+# CONFIG_ZPA2326 is not set
+# CONFIG_ZPOOL is not set
+# CONFIG_ZRAM is not set
+# CONFIG_ZRAM_DEF_COMP_842 is not set
+# CONFIG_ZRAM_DEF_COMP_LZ4 is not set
+# CONFIG_ZRAM_DEF_COMP_LZ4HC is not set
+# CONFIG_ZRAM_DEF_COMP_LZO is not set
+# CONFIG_ZRAM_DEF_COMP_LZORLE is not set
+# CONFIG_ZRAM_DEF_COMP_ZSTD is not set
+# CONFIG_ZRAM_MEMORY_TRACKING is not set
+# CONFIG_ZRAM_MULTI_COMP is not set
+# CONFIG_ZSMALLOC is not set
+CONFIG_ZSMALLOC_CHAIN_SIZE=8
+# CONFIG_ZSWAP is not set
diff --git a/target/linux/generic/hack-6.6/200-tools_portability.patch b/target/linux/generic/hack-6.6/200-tools_portability.patch
new file mode 100644 (file)
index 0000000..f016e64
--- /dev/null
@@ -0,0 +1,257 @@
+From a7ae4ed0a3951c45d4a59ee575951b64ae4a23fb Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 7 May 2024 12:22:15 +0200
+Subject: [PATCH] kernel: fix tools build breakage on macos with x86
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+--- a/tools/scripts/Makefile.include
++++ b/tools/scripts/Makefile.include
+@@ -72,8 +72,6 @@ $(call allow-override,CXX,$(CROSS_COMPIL
+ $(call allow-override,STRIP,$(CROSS_COMPILE)strip)
+ endif
+-CC_NO_CLANG := $(shell $(CC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?)
+-
+ ifneq ($(LLVM),)
+ HOSTAR  ?= $(LLVM_PREFIX)llvm-ar$(LLVM_SUFFIX)
+ HOSTCC  ?= $(LLVM_PREFIX)clang$(LLVM_SUFFIX)
+@@ -84,6 +82,9 @@ HOSTCC  ?= gcc
+ HOSTLD  ?= ld
+ endif
++CC_NO_CLANG := $(shell $(CC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?)
++HOSTCC_NO_CLANG := $(shell $(HOSTCC) -dM -E -x c /dev/null | grep -Fq "__clang__"; echo $$?)
++
+ # Some tools require Clang, LLC and/or LLVM utils
+ CLANG         ?= clang
+ LLC           ?= llc
+@@ -92,8 +93,9 @@ LLVM_OBJCOPY ?= llvm-objcopy
+ LLVM_STRIP    ?= llvm-strip
+ ifeq ($(CC_NO_CLANG), 1)
+-EXTRA_WARNINGS += -Wstrict-aliasing=3
+-
++  ifeq ($(HOSTCC_NO_CLANG), 1)
++    EXTRA_WARNINGS += -Wstrict-aliasing=3
++  endif
+ else ifneq ($(CROSS_COMPILE),)
+ # Allow userspace to override CLANG_CROSS_FLAGS to specify their own
+ # sysroots and flags or to avoid the GCC call in pure Clang builds.
+--- 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;
+@@ -56,6 +60,7 @@ typedef __s8  s8;
+ #define __user
+ #endif
+ #define __must_check
++#undef __cold
+ #define __cold
+ typedef __u16 __bitwise __le16;
+--- a/tools/objtool/include/objtool/objtool.h
++++ b/tools/objtool/include/objtool/objtool.h
+@@ -12,6 +12,7 @@
+ #include <objtool/elf.h>
++#undef __weak
+ #define __weak __attribute__((weak))
+ struct pv_state {
+--- a/tools/include/asm-generic/bitops/fls.h
++++ b/tools/include/asm-generic/bitops/fls.h
+@@ -2,6 +2,8 @@
+ #ifndef _ASM_GENERIC_BITOPS_FLS_H_
+ #define _ASM_GENERIC_BITOPS_FLS_H_
++#include <string.h>
++
+ /**
+  * fls - find last (most-significant) bit set
+  * @x: the word to search
+@@ -10,6 +12,7 @@
+  * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
+  */
++#define fls __linux_fls
+ static __always_inline int fls(unsigned int x)
+ {
+       int r = 32;
+--- a/tools/lib/string.c
++++ b/tools/lib/string.c
+@@ -96,6 +96,7 @@ int strtobool(const char *s, bool *res)
+  * If libc has strlcpy() then that version will override this
+  * implementation:
+  */
++#ifndef __APPLE__
+ #ifdef __clang__
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wignored-attributes"
+@@ -114,6 +115,7 @@ size_t __weak strlcpy(char *dest, const
+ #ifdef __clang__
+ #pragma clang diagnostic pop
+ #endif
++#endif
+ /**
+  * skip_spaces - Removes leading whitespace from @str.
+--- a/tools/arch/x86/include/asm/insn.h
++++ b/tools/arch/x86/include/asm/insn.h
+@@ -7,7 +7,7 @@
+  * Copyright (C) IBM Corporation, 2009
+  */
+-#include <asm/byteorder.h>
++#include <linux/kernel.h>
+ /* insn_attr_t is defined in inat.h */
+ #include "inat.h" /* __ignore_sync_check__ */
+--- a/tools/arch/x86/include/asm/orc_types.h
++++ b/tools/arch/x86/include/asm/orc_types.h
+@@ -46,7 +46,6 @@
+ #define ORC_TYPE_REGS_PARTIAL         4
+ #ifndef __ASSEMBLY__
+-#include <asm/byteorder.h>
+ /*
+  * This struct is more or less a vastly simplified version of the DWARF Call
+@@ -59,12 +58,12 @@
+ struct orc_entry {
+       s16             sp_offset;
+       s16             bp_offset;
+-#if defined(__LITTLE_ENDIAN_BITFIELD)
++#if __BYTE_ORDER == __LITTLE_ENDIAN
+       unsigned        sp_reg:4;
+       unsigned        bp_reg:4;
+       unsigned        type:3;
+       unsigned        signal:1;
+-#elif defined(__BIG_ENDIAN_BITFIELD)
++#elif __BYTE_ORDER == __BIG_ENDIAN
+       unsigned        bp_reg:4;
+       unsigned        sp_reg:4;
+       unsigned        unused:4;
+--- a/tools/include/linux/rbtree.h
++++ b/tools/include/linux/rbtree.h
+@@ -18,7 +18,6 @@
+ #define __TOOLS_LINUX_PERF_RBTREE_H
+ #include <linux/kernel.h>
+-#include <linux/stddef.h>
+ struct rb_node {
+       unsigned long  __rb_parent_color;
+--- 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,18 @@
++#ifndef __LINUX_TYPES_H
++#define __LINUX_TYPES_H
++
++#include <stdint.h>
++
++typedef int8_t __s8;
++typedef uint8_t __u8;
++
++typedef int16_t __s16;
++typedef uint16_t __u16;
++
++typedef int32_t __s32;
++typedef uint32_t __u32;
++
++typedef int64_t __s64;
++typedef uint64_t __u64;
++
++#endif
+--- a/tools/objtool/Makefile
++++ b/tools/objtool/Makefile
+@@ -39,6 +39,8 @@ OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBS
+ elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - | grep elf_getshdr)
+ OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
++OBJTOOL_CFLAGS += $(HOST_EXTRACFLAGS)
++
+ # Always want host compilation.
+ HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
+--- a/tools/objtool/orc_dump.c
++++ b/tools/objtool/orc_dump.c
+@@ -4,10 +4,10 @@
+  */
+ #include <unistd.h>
+-#include <asm/orc_types.h>
+ #include <objtool/objtool.h>
+ #include <objtool/warn.h>
+ #include <objtool/endianness.h>
++#include <asm/orc_types.h>
+ static const char *reg_name(unsigned int reg)
+ {
+--- a/tools/objtool/orc_gen.c
++++ b/tools/objtool/orc_gen.c
+@@ -7,11 +7,11 @@
+ #include <string.h>
+ #include <linux/objtool_types.h>
+-#include <asm/orc_types.h>
+ #include <objtool/check.h>
+ #include <objtool/warn.h>
+ #include <objtool/endianness.h>
++#include <asm/orc_types.h>
+ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
+                         struct instruction *insn)
+--- a/tools/arch/x86/lib/insn.c
++++ b/tools/arch/x86/lib/insn.c
+@@ -15,7 +15,11 @@
+ #include "../include/asm/insn.h" /* __ignore_sync_check__ */
+ #include "../include/asm-generic/unaligned.h" /* __ignore_sync_check__ */
++#ifdef __KERNEL__
+ #include <linux/errno.h>
++#else
++#include <errno.h>
++#endif
+ #include <linux/kconfig.h>
+ #include "../include/asm/emulate_prefix.h" /* __ignore_sync_check__ */
diff --git a/target/linux/generic/hack-6.6/204-module_strip.patch b/target/linux/generic/hack-6.6/204-module_strip.patch
new file mode 100644 (file)
index 0000000..9eadd2c
--- /dev/null
@@ -0,0 +1,210 @@
+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, __stringify(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/kernel/module/Kconfig
++++ b/kernel/module/Kconfig
+@@ -389,4 +389,11 @@ config MODULES_TREE_LOOKUP
+       def_bool y
+       depends on PERF_EVENTS || TRACING || CFI_CLANG
++config MODULE_STRIPPED
++      bool "Reduce module size"
++      depends on MODULES
++      help
++        Remove module parameter descriptions, author info, version, aliases,
++        device tables, etc.
++
+ endif # MODULES
+--- a/kernel/module/main.c
++++ b/kernel/module/main.c
+@@ -997,6 +997,7 @@ size_t modinfo_attrs_count = ARRAY_SIZE(
+ static const char vermagic[] = VERMAGIC_STRING;
++#if defined(CONFIG_MODVERSIONS) || !defined(CONFIG_MODULE_STRIPPED)
+ int try_to_force_load(struct module *mod, const char *reason)
+ {
+ #ifdef CONFIG_MODULE_FORCE_LOAD
+@@ -1008,6 +1009,7 @@ int try_to_force_load(struct module *mod
+       return -ENOEXEC;
+ #endif
+ }
++#endif
+ /* Parse tag=value strings from .modinfo section */
+ char *module_next_tag_pair(char *string, unsigned long *secsize)
+@@ -2075,9 +2077,11 @@ static void module_augment_kernel_taints
+ 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;
+@@ -2091,6 +2095,7 @@ static int check_modinfo(struct module *
+                      info->name, modmagic, vermagic);
+               return -ENOEXEC;
+       }
++#endif
+       err = check_modinfo_livepatch(mod, info);
+       if (err)
+--- a/scripts/mod/modpost.c
++++ b/scripts/mod/modpost.c
+@@ -1666,7 +1666,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
+       }
+       check_sec_ref(mod, &info);
+@@ -1839,8 +1841,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");
+@@ -1854,8 +1858,10 @@ static void add_header(struct buffer *b,
+       buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n");
+       buf_printf(b, "};\n");
++#ifndef CONFIG_MODULE_STRIPPED
+       if (!external_module)
+               buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
++#endif
+       buf_printf(b,
+                  "\n"
+@@ -1863,8 +1869,10 @@ static void add_header(struct buffer *b,
+                  "MODULE_INFO(retpoline, \"Y\");\n"
+                  "#endif\n");
++#ifndef CONFIG_MODULE_STRIPPED
+       if (strstarts(mod->name, "drivers/staging"))
+               buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
++#endif
+       if (strstarts(mod->name, "tools/testing"))
+               buf_printf(b, "\nMODULE_INFO(test, \"Y\");\n");
+@@ -1974,11 +1982,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)
+@@ -2061,7 +2071,9 @@ static void write_mod_c_file(struct modu
+       add_exported_symbols(&buf, mod);
+       add_versions(&buf, mod);
+       add_depends(&buf, mod);
++#ifndef CONFIG_MODULE_STRIPPED
+       add_moddevtable(&buf, mod);
++#endif
+       add_srcversion(&buf, mod);
+       ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name);
diff --git a/target/linux/generic/hack-6.6/205-kconfig-abort-configuration-on-unset-symbol.patch b/target/linux/generic/hack-6.6/205-kconfig-abort-configuration-on-unset-symbol.patch
new file mode 100644 (file)
index 0000000..df18507
--- /dev/null
@@ -0,0 +1,41 @@
+From 310e8e04a05d9eb43fa9dd7f00143300afcaa37a Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Fri, 11 Nov 2022 13:33:44 +0100
+Subject: [PATCH] kconfig: abort configuration on unset symbol
+
+When a target configuration has unset Kconfig symbols, the build will
+fail when OpenWrt is compiled with V=s and stdin is connected to a tty.
+
+In case OpenWrt is compiled without either of these preconditions, the
+build will succeed with the symbols in question being unset.
+
+Modify the kernel configuration in a way it fails on unset symbols
+regardless of the aforementioned preconditions.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ scripts/kconfig/conf.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/scripts/kconfig/conf.c
++++ b/scripts/kconfig/conf.c
+@@ -338,6 +338,9 @@ static int conf_askvalue(struct symbol *
+               }
+               /* fall through */
+       default:
++              if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) {
++                      exit(1);
++              }
+               fflush(stdout);
+               xfgets(line, sizeof(line), stdin);
+               break;
+@@ -520,6 +523,9 @@ static int conf_choice(struct menu *menu
+                       }
+                       /* fall through */
+               case oldaskconfig:
++                      if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) {
++                              exit(1);
++                      }
+                       fflush(stdout);
+                       xfgets(line, sizeof(line), stdin);
+                       strip(line);
diff --git a/target/linux/generic/hack-6.6/210-darwin_scripts_include.patch b/target/linux/generic/hack-6.6/210-darwin_scripts_include.patch
new file mode 100644 (file)
index 0000000..b94554f
--- /dev/null
@@ -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
+@@ -9,7 +9,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 "../../include/linux/module_symbol.h"
+ #include "list.h"
diff --git a/target/linux/generic/hack-6.6/211-darwin-uuid-typedef-clash.patch b/target/linux/generic/hack-6.6/211-darwin-uuid-typedef-clash.patch
new file mode 100644 (file)
index 0000000..c0e0b24
--- /dev/null
@@ -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
+@@ -35,6 +35,9 @@ typedef uint32_t     __u32;
+ typedef uint16_t      __u16;
+ typedef unsigned char __u8;
++#ifdef __APPLE__
++#define uuid_t compat_uuid_t
++#endif
+ /* UUID types for backward compatibility, don't use in new code */
+ typedef struct {
+       __u8 b[16];
diff --git a/target/linux/generic/hack-6.6/214-spidev_h_portability.patch b/target/linux/generic/hack-6.6/214-spidev_h_portability.patch
new file mode 100644 (file)
index 0000000..db754a2
--- /dev/null
@@ -0,0 +1,24 @@
+From be9be95ff10e16a5b4ad36f903978d0cc5747024 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:04:08 +0200
+Subject: kernel: fix linux/spi/spidev.h portability issues with musl
+
+Felix will try to get this define included into musl
+
+lede-commit: 795e7cf60de19e7a076a46874fab7bb88b43bbff
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/uapi/linux/spi/spidev.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/uapi/linux/spi/spidev.h
++++ b/include/uapi/linux/spi/spidev.h
+@@ -93,7 +93,7 @@ struct spi_ioc_transfer {
+ /* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */
+ #define SPI_MSGSIZE(N) \
+-      ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \
++      ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << 13)) \
+               ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0)
+ #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
diff --git a/target/linux/generic/hack-6.6/220-arm-gc_sections.patch b/target/linux/generic/hack-6.6/220-arm-gc_sections.patch
new file mode 100644 (file)
index 0000000..eb49704
--- /dev/null
@@ -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
+@@ -128,6 +128,7 @@ config ARM
+       select HOTPLUG_CORE_SYNC_DEAD if HOTPLUG_CPU
+       select IRQ_FORCED_THREADING
+       select LOCK_MM_AND_FIND_VMA
++      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))
+ OBJS  += lib1funcs.o ashldi3.o bswapsdi2.o
+--- a/arch/arm/kernel/vmlinux.lds.S
++++ b/arch/arm/kernel/vmlinux.lds.S
+@@ -74,7 +74,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 = .;
+       }
+@@ -99,24 +99,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                                                   \
+@@ -108,12 +108,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 = .;                                  \
+       }
+@@ -125,7 +125,7 @@
+       __vectors_lma = .;                                              \
+       OVERLAY 0xffff0000 : NOCROSSREFS AT(__vectors_lma) {            \
+               .vectors {                                              \
+-                      *(.vectors)                                     \
++                      KEEP(*(.vectors))                               \
+               }                                                       \
+               .vectors.bhb.loop8 {                                    \
+                       *(.vectors.bhb.loop8)                           \
+@@ -143,7 +143,7 @@
+                                                                       \
+       __stubs_lma = .;                                                \
+       .stubs ADDR(.vectors) + 0x1000 : AT(__stubs_lma) {              \
+-              *(.stubs)                                               \
++              KEEP(*(.stubs))                                         \
+       }                                                               \
+       ARM_LMA(__stubs, .stubs);                                       \
+       . = __stubs_lma + SIZEOF(.stubs);                               \
diff --git a/target/linux/generic/hack-6.6/230-openwrt_lzma_options.patch b/target/linux/generic/hack-6.6/230-openwrt_lzma_options.patch
new file mode 100644 (file)
index 0000000..ca70da7
--- /dev/null
@@ -0,0 +1,38 @@
+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
+@@ -460,10 +460,10 @@ quiet_cmd_bzip2_with_size = BZIP2   $@
+ # ---------------------------------------------------------------------------
+ quiet_cmd_lzma = LZMA    $@
+-      cmd_lzma = cat $(real-prereqs) | $(LZMA) -9 > $@
++      cmd_lzma = cat $(real-prereqs) | $(LZMA) e -d20 -lc1 -lp2 -pb2 -eos -si -so > $@
+ quiet_cmd_lzma_with_size = LZMA    $@
+-      cmd_lzma_with_size = { cat $(real-prereqs) | $(LZMA) -9; $(size_append); } > $@
++      cmd_lzma_with_size = { 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 > $@
diff --git a/target/linux/generic/hack-6.6/250-netfilter_depends.patch b/target/linux/generic/hack-6.6/250-netfilter_depends.patch
new file mode 100644 (file)
index 0000000..43faa99
--- /dev/null
@@ -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
+@@ -259,7 +259,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
+@@ -1120,7 +1119,6 @@ config NETFILTER_XT_TARGET_SECMARK
+ config NETFILTER_XT_TARGET_TCPMSS
+       tristate '"TCPMSS" target support'
+-      depends on IPV6 || IPV6=n
+       default m if NETFILTER_ADVANCED=n
+       help
+         This option adds a `TCPMSS' target, which allows you to alter the
diff --git a/target/linux/generic/hack-6.6/251-kconfig.patch b/target/linux/generic/hack-6.6/251-kconfig.patch
new file mode 100644 (file)
index 0000000..845cfbf
--- /dev/null
@@ -0,0 +1,157 @@
+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
+@@ -55,7 +55,7 @@ config CRYPTO_FIPS_VERSION
+         By default the KERNELRELEASE value is used.
+ config CRYPTO_ALGAPI
+-      tristate
++      tristate "ALGAPI"
+       select CRYPTO_ALGAPI2
+       help
+         This option provides the API for cryptographic algorithms.
+@@ -64,7 +64,7 @@ config CRYPTO_ALGAPI2
+       tristate
+ config CRYPTO_AEAD
+-      tristate
++      tristate "AEAD"
+       select CRYPTO_AEAD2
+       select CRYPTO_ALGAPI
+@@ -82,7 +82,7 @@ config CRYPTO_SIG2
+       select CRYPTO_ALGAPI2
+ config CRYPTO_SKCIPHER
+-      tristate
++      tristate "SKCIPHER"
+       select CRYPTO_SKCIPHER2
+       select CRYPTO_ALGAPI
+@@ -91,7 +91,7 @@ config CRYPTO_SKCIPHER2
+       select CRYPTO_ALGAPI2
+ config CRYPTO_HASH
+-      tristate
++      tristate "HASH"
+       select CRYPTO_HASH2
+       select CRYPTO_ALGAPI
+@@ -100,7 +100,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
+@@ -460,16 +460,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
+@@ -22,7 +22,7 @@ config NETFILTER_SKIP_EGRESS
+       def_bool NETFILTER_EGRESS && (NET_CLS_ACT || IFB)
+ config NETFILTER_NETLINK
+-      tristate
++      tristate "Netfilter NFNETLINK interface"
+ config NETFILTER_FAMILY_BRIDGE
+       bool
+--- 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
+@@ -40,7 +40,7 @@ config SND_UMP_LEGACY_RAWMIDI
+         The device contains 16 substreams corresponding to UMP groups.
+ config SND_COMPRESS_OFFLOAD
+-      tristate
++      tristate "Compression offloading support"
+ config SND_JACK
+       bool
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -467,7 +467,7 @@ config NET_DEVLINK
+       default n
+ config PAGE_POOL
+-      bool
++      bool "Page pool support"
+ config PAGE_POOL_STATS
+       default n
diff --git a/target/linux/generic/hack-6.6/253-ksmbd-config.patch b/target/linux/generic/hack-6.6/253-ksmbd-config.patch
new file mode 100644 (file)
index 0000000..d54b488
--- /dev/null
@@ -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
+@@ -2002,7 +2002,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
+@@ -641,7 +641,7 @@ config LIBFDT
+       bool
+ config OID_REGISTRY
+-      tristate
++      tristate "OID"
+       help
+         Enable fast lookup object identifier registry.
diff --git a/target/linux/generic/hack-6.6/259-regmap_dynamic.patch b/target/linux/generic/hack-6.6/259-regmap_dynamic.patch
new file mode 100644 (file)
index 0000000..cb93c96
--- /dev/null
@@ -0,0 +1,156 @@
+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,8 +4,7 @@
+ # subsystems should select the appropriate symbols.
+ config REGMAP
+-      bool
+-      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 || REGMAP_FSI)
++      tristate
+       select IRQ_DOMAIN if REGMAP_IRQ
+       select MDIO_BUS if REGMAP_MDIO
+       help
+@@ -19,7 +18,7 @@ config REGMAP
+ config REGMAP_KUNIT
+       tristate "KUnit tests for regmap"
+-      depends on KUNIT && REGMAP
++      depends on KUNIT
+       default KUNIT_ALL_TESTS
+       select REGMAP_RAM
+@@ -34,60 +33,76 @@ config REGMAP_BUILD
+         normally enabled.
+ 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_RAM
++      select REGMAP
+       tristate
+ 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
+ config REGMAP_FSI
++      select REGMAP
+       tristate
+       depends on FSI
+--- a/drivers/base/regmap/Makefile
++++ b/drivers/base/regmap/Makefile
+@@ -2,9 +2,11 @@
+ # 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 regcache-maple.o
+-obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
++regmap-core-objs = regmap.o regcache.o regcache-rbtree.o regcache-flat.o regcache-maple.o
++ifdef CONFIG_DEBUG_FS
++regmap-core-objs += regmap-debugfs.o
++endif
++obj-$(CONFIG_REGMAP) += regmap-core.o
+ obj-$(CONFIG_REGMAP_KUNIT) += regmap-kunit.o
+ obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
+ obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.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>
+@@ -3470,3 +3471,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
+@@ -197,7 +197,7 @@ struct reg_sequence {
+       __ret ?: __tmp; \
+ })
+-#ifdef CONFIG_REGMAP
++#if IS_REACHABLE(CONFIG_REGMAP)
+ enum regmap_endian {
+       /* Unspecified -> 0 -> Backwards compatible default */
diff --git a/target/linux/generic/hack-6.6/260-crypto_test_dependencies.patch b/target/linux/generic/hack-6.6/260-crypto_test_dependencies.patch
new file mode 100644 (file)
index 0000000..6221d0f
--- /dev/null
@@ -0,0 +1,54 @@
+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
+@@ -148,15 +148,15 @@ config CRYPTO_MANAGER
+         cbc(aes).
+ config CRYPTO_MANAGER2
+-      def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y)
+-      select CRYPTO_ACOMP2
+-      select CRYPTO_AEAD2
+-      select CRYPTO_AKCIPHER2
+-      select CRYPTO_SIG2
+-      select CRYPTO_HASH2
+-      select CRYPTO_KPP2
+-      select CRYPTO_RNG2
+-      select CRYPTO_SKCIPHER2
++      def_tristate CRYPTO_MANAGER || (CRYPTO_MANAGER!=n && CRYPTO_ALGAPI=y && !CRYPTO_MANAGER_DISABLE_TESTS)
++      select CRYPTO_ACOMP2 if !CRYPTO_MANAGER_DISABLE_TESTS
++      select CRYPTO_AEAD2 if !CRYPTO_MANAGER_DISABLE_TESTS
++      select CRYPTO_AKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS
++      select CRYPTO_SIG2 if !CRYPTO_MANAGER_DISABLE_TESTS
++      select CRYPTO_HASH2 if !CRYPTO_MANAGER_DISABLE_TESTS
++      select CRYPTO_KPP2 if !CRYPTO_MANAGER_DISABLE_TESTS
++      select CRYPTO_RNG2 if !CRYPTO_MANAGER_DISABLE_TESTS
++      select CRYPTO_SKCIPHER2 if !CRYPTO_MANAGER_DISABLE_TESTS
+ config CRYPTO_USER
+       tristate "Userspace cryptographic algorithm configuration"
+--- a/crypto/algboss.c
++++ b/crypto/algboss.c
+@@ -204,6 +204,10 @@ static int cryptomgr_schedule_test(struc
+       memcpy(param->alg, alg->cra_name, sizeof(param->alg));
+       param->type = alg->cra_flags;
++#ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS
++      param->type |= CRYPTO_ALG_TESTED;
++#endif
++
+       thread = kthread_run(cryptomgr_test, param, "cryptomgr_test");
+       if (IS_ERR(thread))
+               goto err_free_param;
diff --git a/target/linux/generic/hack-6.6/261-lib-arc4-unhide.patch b/target/linux/generic/hack-6.6/261-lib-arc4-unhide.patch
new file mode 100644 (file)
index 0000000..af1d286
--- /dev/null
@@ -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
+@@ -15,7 +15,7 @@ config CRYPTO_LIB_AESGCM
+       select CRYPTO_LIB_UTILS
+ config CRYPTO_LIB_ARC4
+-      tristate
++      tristate "ARC4 cipher library"
+ config CRYPTO_LIB_GF128MUL
+       tristate
diff --git a/target/linux/generic/hack-6.6/280-rfkill-stubs.patch b/target/linux/generic/hack-6.6/280-rfkill-stubs.patch
new file mode 100644 (file)
index 0000000..7a650d1
--- /dev/null
@@ -0,0 +1,84 @@
+From 236c1acdfef5958010ac9814a9872e0a46fd78ee Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Fri, 7 Jul 2017 17:13:44 +0200
+Subject: rfkill: add fake rfkill support
+
+allow building of modules depending on RFKILL even if RFKILL is not enabled.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ include/linux/rfkill.h |  2 +-
+ net/Makefile           |  2 +-
+ net/rfkill/Kconfig     | 14 +++++++++-----
+ net/rfkill/Makefile    |  2 +-
+ 4 files changed, 12 insertions(+), 8 deletions(-)
+
+--- a/include/linux/rfkill.h
++++ b/include/linux/rfkill.h
+@@ -64,7 +64,7 @@ struct rfkill_ops {
+       int     (*set_block)(void *data, bool blocked);
+ };
+-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
++#if defined(CONFIG_RFKILL_FULL) || defined(CONFIG_RFKILL_FULL_MODULE)
+ /**
+  * rfkill_alloc - Allocate rfkill structure
+  * @name: name of the struct -- the string is not copied internally
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -52,7 +52,7 @@ obj-$(CONFIG_TIPC)           += tipc/
+ obj-$(CONFIG_NETLABEL)                += netlabel/
+ obj-$(CONFIG_IUCV)            += iucv/
+ obj-$(CONFIG_SMC)             += smc/
+-obj-$(CONFIG_RFKILL)          += rfkill/
++obj-$(CONFIG_RFKILL_FULL)     += rfkill/
+ obj-$(CONFIG_NET_9P)          += 9p/
+ obj-$(CONFIG_CAIF)            += caif/
+ obj-$(CONFIG_DCB)             += dcb/
+--- a/net/rfkill/Kconfig
++++ b/net/rfkill/Kconfig
+@@ -2,7 +2,11 @@
+ #
+ # RF switch subsystem configuration
+ #
+-menuconfig RFKILL
++config RFKILL
++      bool
++      default y
++
++menuconfig RFKILL_FULL
+       tristate "RF switch subsystem support"
+       help
+         Say Y here if you want to have control over RF switches
+@@ -14,19 +18,19 @@ menuconfig RFKILL
+ # LED trigger support
+ config RFKILL_LEDS
+       bool
+-      depends on RFKILL
++      depends on RFKILL_FULL
+       depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS
+       default y
+ config RFKILL_INPUT
+       bool "RF switch input support" if EXPERT
+-      depends on RFKILL
++      depends on RFKILL_FULL
+       depends on INPUT = y || RFKILL = INPUT
+       default y if !EXPERT
+ config RFKILL_GPIO
+       tristate "GPIO RFKILL driver"
+-      depends on RFKILL
++      depends on RFKILL_FULL
+       depends on GPIOLIB || COMPILE_TEST
+       default n
+       help
+--- a/net/rfkill/Makefile
++++ b/net/rfkill/Makefile
+@@ -5,5 +5,5 @@
+ rfkill-y                      += core.o
+ rfkill-$(CONFIG_RFKILL_INPUT) += input.o
+-obj-$(CONFIG_RFKILL)          += rfkill.o
++obj-$(CONFIG_RFKILL_FULL)     += rfkill.o
+ obj-$(CONFIG_RFKILL_GPIO)     += rfkill-gpio.o
diff --git a/target/linux/generic/hack-6.6/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch b/target/linux/generic/hack-6.6/300-MIPS-r4k_cache-use-more-efficient-cache-blast.patch
new file mode 100644 (file)
index 0000000..f21f200
--- /dev/null
@@ -0,0 +1,64 @@
+From: Ben Menchaca <ben.menchaca@qca.qualcomm.com>
+Date: Fri, 7 Jun 2013 18:35:22 -0500
+Subject: MIPS: r4k_cache: use more efficient cache blast
+
+Optimize the compiler output for larger cache blast cases that are
+common for DMA-based networking.
+
+Signed-off-by: Ben Menchaca <ben.menchaca@qca.qualcomm.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+--- a/arch/mips/include/asm/r4kcache.h
++++ b/arch/mips/include/asm/r4kcache.h
+@@ -286,14 +286,46 @@ static inline void prot##extra##blast_##
+                                                   unsigned long end)  \
+ {                                                                     \
+       unsigned long lsize = cpu_##desc##_line_size();                 \
++      unsigned long lsize_2 = lsize * 2;                              \
++      unsigned long lsize_3 = lsize * 3;                              \
++      unsigned long lsize_4 = lsize * 4;                              \
++      unsigned long lsize_5 = lsize * 5;                              \
++      unsigned long lsize_6 = lsize * 6;                              \
++      unsigned long lsize_7 = lsize * 7;                              \
++      unsigned long lsize_8 = lsize * 8;                              \
+       unsigned long addr = start & ~(lsize - 1);                      \
+-      unsigned long aend = (end - 1) & ~(lsize - 1);                  \
++      unsigned long aend = (end + lsize - 1) & ~(lsize - 1);          \
++      int lines = (aend - addr) / lsize;                              \
+                                                                       \
+-      while (1) {                                                     \
++      while (lines >= 8) {                                            \
++              prot##cache_op(hitop, addr);                            \
++              prot##cache_op(hitop, addr + lsize);                    \
++              prot##cache_op(hitop, addr + lsize_2);                  \
++              prot##cache_op(hitop, addr + lsize_3);                  \
++              prot##cache_op(hitop, addr + lsize_4);                  \
++              prot##cache_op(hitop, addr + lsize_5);                  \
++              prot##cache_op(hitop, addr + lsize_6);                  \
++              prot##cache_op(hitop, addr + lsize_7);                  \
++              addr += lsize_8;                                        \
++              lines -= 8;                                             \
++      }                                                               \
++                                                                      \
++      if (lines & 0x4) {                                              \
++              prot##cache_op(hitop, addr);                            \
++              prot##cache_op(hitop, addr + lsize);                    \
++              prot##cache_op(hitop, addr + lsize_2);                  \
++              prot##cache_op(hitop, addr + lsize_3);                  \
++              addr += lsize_4;                                        \
++      }                                                               \
++                                                                      \
++      if (lines & 0x2) {                                              \
++              prot##cache_op(hitop, addr);                            \
++              prot##cache_op(hitop, addr + lsize);                    \
++              addr += lsize_2;                                        \
++      }                                                               \
++                                                                      \
++      if (lines & 0x1) {                                              \
+               prot##cache_op(hitop, addr);                            \
+-              if (addr == aend)                                       \
+-                      break;                                          \
+-              addr += lsize;                                          \
+       }                                                               \
+ }
diff --git a/target/linux/generic/hack-6.6/402-mtd-blktrans-call-add-disks-after-mtd-device.patch b/target/linux/generic/hack-6.6/402-mtd-blktrans-call-add-disks-after-mtd-device.patch
new file mode 100644 (file)
index 0000000..24b7963
--- /dev/null
@@ -0,0 +1,112 @@
+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
+@@ -386,19 +386,8 @@ int add_mtd_blktrans_dev(struct mtd_blkt
+       if (new->readonly)
+               set_disk_ro(gd, 1);
+-      ret = device_add_disk(&new->mtd->dev, gd, NULL);
+-      if (ret)
+-              goto out_cleanup_disk;
+-
+-      if (new->disk_attributes) {
+-              ret = sysfs_create_group(&disk_to_dev(gd)->kobj,
+-                                      new->disk_attributes);
+-              WARN_ON(ret);
+-      }
+       return 0;
+-out_cleanup_disk:
+-      put_disk(new->disk);
+ out_free_tag_set:
+       blk_mq_free_tag_set(new->tag_set);
+ out_kfree_tag_set:
+@@ -408,6 +397,35 @@ 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;
++
++                      ret = device_add_disk(&dev->mtd->dev, dev->disk, NULL);
++                      if (ret)
++                              goto out_cleanup_disk;
++
++                      if (dev->disk_attributes) {
++                              ret = sysfs_create_group(&disk_to_dev(dev->disk)->kobj,
++                                                      dev->disk_attributes);
++                              WARN_ON(ret);
++                      }
++              }
++      }
++
++      return;
++
++out_cleanup_disk:
++      put_disk(dev->disk);
++}
++
+ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
+ {
+       unsigned long flags;
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -33,6 +33,7 @@
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/mtd/blktrans.h>
+ #include "mtdcore.h"
+@@ -1127,6 +1128,8 @@ int mtd_device_parse_register(struct mtd
+               register_reboot_notifier(&mtd->reboot_notifier);
+       }
++      register_mtd_blktrans_devs();
++
+ out:
+       if (ret) {
+               nvmem_unregister(mtd->otp_user_nvmem);
+--- a/include/linux/mtd/blktrans.h
++++ b/include/linux/mtd/blktrans.h
+@@ -76,6 +76,7 @@ extern int deregister_mtd_blktrans(struc
+ extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
+ extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
+ extern int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev);
++extern void register_mtd_blktrans_devs(void);
+ /**
+  * module_mtd_blktrans() - Helper macro for registering a mtd blktrans driver
diff --git a/target/linux/generic/hack-6.6/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch b/target/linux/generic/hack-6.6/420-mtd-support-OpenWrt-s-MTD_ROOTFS_ROOT_DEV.patch
new file mode 100644 (file)
index 0000000..c32d8ec
--- /dev/null
@@ -0,0 +1,24 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 7 Nov 2022 23:48:24 +0100
+Subject: [PATCH] mtd: support OpenWrt's MTD_ROOTFS_ROOT_DEV
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This allows setting ROOT_DEV to MTD partition named "rootfs".
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -801,7 +801,8 @@ int add_mtd_device(struct mtd_info *mtd)
+       mutex_unlock(&mtd_table_mutex);
+-      if (of_property_read_bool(mtd_get_of_node(mtd), "linux,rootfs")) {
++      if (of_property_read_bool(mtd_get_of_node(mtd), "linux,rootfs") ||
++          (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) && !strcmp(mtd->name, "rootfs") && ROOT_DEV == 0)) {
+               if (IS_BUILTIN(CONFIG_MTD)) {
+                       pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
+                       ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
diff --git a/target/linux/generic/hack-6.6/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch b/target/linux/generic/hack-6.6/421-drivers-mtd-parsers-add-nvmem-support-to-cmdlinepart.patch
new file mode 100644 (file)
index 0000000..965a331
--- /dev/null
@@ -0,0 +1,120 @@
+From 6fa9e3678eb002246df1280322b6a024853950a5 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Mon, 11 Oct 2021 00:53:14 +0200
+Subject: [PATCH] drivers: mtd: parsers: add nvmem support to cmdlinepart
+
+Assuming cmdlinepart is only one level deep partition scheme and that
+static partition are also defined in DTS, we can assign an of_node for
+partition declared from bootargs. cmdlinepart have priority than
+fiexed-partition parser so in this specific case the parser doesn't
+assign an of_node. Fix this by searching a defined of_node using a
+similar fixed_partition parser and if a partition is found with the same
+label, check that it has the same offset and size and return the DT
+of_node to correctly use NVMEM cells.
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+---
+ drivers/mtd/parsers/cmdlinepart.c | 71 +++++++++++++++++++++++++++++++
+ 1 file changed, 71 insertions(+)
+
+--- a/drivers/mtd/parsers/cmdlinepart.c
++++ b/drivers/mtd/parsers/cmdlinepart.c
+@@ -43,6 +43,7 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/module.h>
+ #include <linux/err.h>
++#include <linux/of.h>
+ /* debug macro */
+ #if 0
+@@ -323,6 +324,68 @@ static int mtdpart_setup_real(char *s)
+       return 0;
+ }
++static int search_fixed_partition(struct mtd_info *master,
++                                struct mtd_partition *target_part,
++                                struct mtd_partition *fixed_part)
++{
++      struct device_node *mtd_node;
++      struct device_node *ofpart_node;
++      struct device_node *pp;
++      struct mtd_partition part;
++      const char *partname;
++
++      mtd_node = mtd_get_of_node(master);
++      if (!mtd_node)
++              return -EINVAL;
++
++      ofpart_node = of_get_child_by_name(mtd_node, "partitions");
++
++      for_each_child_of_node(ofpart_node,  pp) {
++              const __be32 *reg;
++              int len;
++              int a_cells, s_cells;
++
++              reg = of_get_property(pp, "reg", &len);
++              if (!reg) {
++                      pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n",
++                               master->name, pp,
++                               mtd_node);
++                      continue;
++              }
++
++              a_cells = of_n_addr_cells(pp);
++              s_cells = of_n_size_cells(pp);
++              if (len / 4 != a_cells + s_cells) {
++                      pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n",
++                               master->name, pp,
++                               mtd_node);
++                      continue;
++              }
++
++              part.offset = of_read_number(reg, a_cells);
++              part.size = of_read_number(reg + a_cells, s_cells);
++              part.of_node = pp;
++
++              partname = of_get_property(pp, "label", &len);
++              if (!partname)
++                      partname = of_get_property(pp, "name", &len);
++              part.name = partname;
++
++              if (!strncmp(target_part->name, part.name, len)) {
++                      if (part.offset != target_part->offset)
++                              return -EINVAL;
++
++                      if (part.size != target_part->size)
++                              return -EINVAL;
++
++                      memcpy(fixed_part, &part, sizeof(struct mtd_partition));
++                      return 0;
++              }
++      }
++
++      return -EINVAL;
++}
++
+ /*
+  * Main function to be called from the MTD mapping driver/device to
+  * obtain the partitioning information. At this point the command line
+@@ -338,6 +401,7 @@ static int parse_cmdline_partitions(stru
+       int i, err;
+       struct cmdline_mtd_partition *part;
+       const char *mtd_id = master->name;
++      struct mtd_partition fixed_part;
+       /* parse command line */
+       if (!cmdline_parsed) {
+@@ -382,6 +446,13 @@ static int parse_cmdline_partitions(stru
+                               sizeof(*part->parts) * (part->num_parts - i));
+                       i--;
+               }
++
++              err = search_fixed_partition(master, &part->parts[i], &fixed_part);
++              if (!err) {
++                      part->parts[i].of_node = fixed_part.of_node;
++                      pr_info("Found partition defined in DT for %s. Assigning OF node to support nvmem.",
++                              part->parts[i].name);
++              }
+       }
+       *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
diff --git a/target/linux/generic/hack-6.6/430-mtk-bmt-support.patch b/target/linux/generic/hack-6.6/430-mtk-bmt-support.patch
new file mode 100644 (file)
index 0000000..1e69ee6
--- /dev/null
@@ -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
++
+ config MTD_NAND_ECC_MXIC
+       bool "Macronix external hardware ECC engine"
+       depends on HAS_IOMEM
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -3,6 +3,7 @@
+ nandcore-objs := core.o bbt.o
+ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
+ obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o
++obj-$(CONFIG_MTD_NAND_MTK_BMT)        += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o
+ obj-y += onenand/
+ obj-y += raw/
diff --git a/target/linux/generic/hack-6.6/600-net-enable-fraglist-GRO-by-default.patch b/target/linux/generic/hack-6.6/600-net-enable-fraglist-GRO-by-default.patch
new file mode 100644 (file)
index 0000000..9633525
--- /dev/null
@@ -0,0 +1,24 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 23 Apr 2024 12:35:21 +0200
+Subject: [PATCH] net: enable fraglist GRO by default
+
+This can significantly improve performance for packet forwarding/bridging
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/linux/netdev_features.h
++++ b/include/linux/netdev_features.h
+@@ -243,10 +243,10 @@ static inline int find_next_netdev_featu
+ #define NETIF_F_UPPER_DISABLES        NETIF_F_LRO
+ /* changeable features with no special hardware requirements */
+-#define NETIF_F_SOFT_FEATURES (NETIF_F_GSO | NETIF_F_GRO)
++#define NETIF_F_SOFT_FEATURES (NETIF_F_GSO | NETIF_F_GRO | NETIF_F_GRO_FRAGLIST)
+ /* Changeable features with no special hardware requirements that defaults to off. */
+-#define NETIF_F_SOFT_FEATURES_OFF     (NETIF_F_GRO_FRAGLIST | NETIF_F_GRO_UDP_FWD)
++#define NETIF_F_SOFT_FEATURES_OFF     (NETIF_F_GRO_UDP_FWD)
+ #define NETIF_F_VLAN_FEATURES (NETIF_F_HW_VLAN_CTAG_FILTER | \
+                                NETIF_F_HW_VLAN_CTAG_RX | \
diff --git a/target/linux/generic/hack-6.6/645-netfilter-connmark-introduce-set-dscpmark.patch b/target/linux/generic/hack-6.6/645-netfilter-connmark-introduce-set-dscpmark.patch
new file mode 100644 (file)
index 0000000..bb80285
--- /dev/null
@@ -0,0 +1,231 @@
+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
+@@ -15,6 +15,11 @@ enum {
+ };
+ enum {
++      XT_CONNMARK_VALUE =     (1 << 0),
++      XT_CONNMARK_DSCP =      (1 << 1)
++};
++
++enum {
+       D_SHIFT_LEFT = 0,
+       D_SHIFT_RIGHT,
+ };
+@@ -29,6 +34,11 @@ struct xt_connmark_tginfo2 {
+       __u8 shift_dir, shift_bits, mode;
+ };
++struct xt_connmark_tginfo3 {
++      __u32 ctmark, ctmask, nfmask;
++      __u8 shift_dir, shift_bits, mode, func;
++};
++
+ struct xt_connmark_mtinfo1 {
+       __u32 mark, mask;
+       __u8 invert;
+--- a/net/netfilter/xt_connmark.c
++++ b/net/netfilter/xt_connmark.c
+@@ -24,13 +24,13 @@ MODULE_ALIAS("ipt_connmark");
+ MODULE_ALIAS("ip6t_connmark");
+ static unsigned int
+-connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
++connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo3 *info)
+ {
+       enum ip_conntrack_info ctinfo;
+       u_int32_t new_targetmark;
+       struct nf_conn *ct;
+       u_int32_t newmark;
+-      u_int32_t oldmark;
++      u_int8_t dscp;
+       ct = nf_ct_get(skb, &ctinfo);
+       if (ct == NULL)
+@@ -38,13 +38,24 @@ connmark_tg_shift(struct sk_buff *skb, c
+       switch (info->mode) {
+       case XT_CONNMARK_SET:
+-              oldmark = READ_ONCE(ct->mark);
+-              newmark = (oldmark & ~info->ctmask) ^ info->ctmark;
+-              if (info->shift_dir == D_SHIFT_RIGHT)
+-                      newmark >>= info->shift_bits;
+-              else
+-                      newmark <<= info->shift_bits;
++              newmark = READ_ONCE(ct->mark);
++              if (info->func & XT_CONNMARK_VALUE) {
++                      newmark = (newmark & ~info->ctmask) ^ info->ctmark;
++                      if (info->shift_dir == D_SHIFT_RIGHT)
++                              newmark >>= info->shift_bits;
++                      else
++                              newmark <<= info->shift_bits;
++              } else if (info->func & XT_CONNMARK_DSCP) {
++                      if (skb->protocol == htons(ETH_P_IP))
++                              dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2;
++                      else if (skb->protocol == htons(ETH_P_IPV6))
++                              dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2;
++                      else    /* protocol doesn't have diffserv */
++                              break;
++                      newmark = (newmark & ~info->ctmark) |
++                                (info->ctmask | (dscp << info->shift_bits));
++              }
+               if (READ_ONCE(ct->mark) != newmark) {
+                       WRITE_ONCE(ct->mark, newmark);
+                       nf_conntrack_event_cache(IPCT_MARK, ct);
+@@ -83,20 +94,36 @@ static unsigned int
+ connmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
+ {
+       const struct xt_connmark_tginfo1 *info = par->targinfo;
+-      const struct xt_connmark_tginfo2 info2 = {
++      const struct xt_connmark_tginfo3 info3 = {
+               .ctmark = info->ctmark,
+               .ctmask = info->ctmask,
+               .nfmask = info->nfmask,
+               .mode   = info->mode,
++              .func   = XT_CONNMARK_VALUE
+       };
+-      return connmark_tg_shift(skb, &info2);
++      return connmark_tg_shift(skb, &info3);
+ }
+ static unsigned int
+ connmark_tg_v2(struct sk_buff *skb, const struct xt_action_param *par)
+ {
+       const struct xt_connmark_tginfo2 *info = par->targinfo;
++      const struct xt_connmark_tginfo3 info3 = {
++              .ctmark = info->ctmark,
++              .ctmask = info->ctmask,
++              .nfmask = info->nfmask,
++              .mode   = info->mode,
++              .func   = XT_CONNMARK_VALUE
++      };
++
++      return connmark_tg_shift(skb, &info3);
++}
++
++static unsigned int
++connmark_tg_v3(struct sk_buff *skb, const struct xt_action_param *par)
++{
++      const struct xt_connmark_tginfo3 *info = par->targinfo;
+       return connmark_tg_shift(skb, info);
+ }
+@@ -168,6 +195,16 @@ static struct xt_target connmark_tg_reg[
+               .destroy        = connmark_tg_destroy,
+               .me             = THIS_MODULE,
+       },
++      {
++              .name           = "CONNMARK",
++              .revision       = 3,
++              .family         = NFPROTO_IPV4,
++              .checkentry     = connmark_tg_check,
++              .target         = connmark_tg_v3,
++              .targetsize     = sizeof(struct xt_connmark_tginfo3),
++              .destroy        = connmark_tg_destroy,
++              .me             = THIS_MODULE,
++      },
+ #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
+       {
+               .name           = "CONNMARK",
+@@ -189,6 +226,16 @@ static struct xt_target connmark_tg_reg[
+               .destroy        = connmark_tg_destroy,
+               .me             = THIS_MODULE,
+       },
++      {
++              .name           = "CONNMARK",
++              .revision       = 3,
++              .family         = NFPROTO_IPV6,
++              .checkentry     = connmark_tg_check,
++              .target         = connmark_tg_v3,
++              .targetsize     = sizeof(struct xt_connmark_tginfo3),
++              .destroy        = connmark_tg_destroy,
++              .me             = THIS_MODULE,
++      },
+ #endif
+ };
diff --git a/target/linux/generic/hack-6.6/650-netfilter-add-xt_FLOWOFFLOAD-target.patch b/target/linux/generic/hack-6.6/650-netfilter-add-xt_FLOWOFFLOAD-target.patch
new file mode 100644 (file)
index 0000000..eca611d
--- /dev/null
@@ -0,0 +1,812 @@
+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/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -729,7 +729,6 @@ 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.
+@@ -1025,6 +1024,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
+@@ -163,6 +163,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,703 @@
++/*
++ * 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):
++              if (!nf_flow_pppoe_proto(skb, &proto))
++                      return NF_ACCEPT;
++              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;
++      }
++
++      if (!dst_hold_safe(this_dst))
++              return -ENOENT;
++
++      nf_route(xt_net(par), &other_dst, &fl, false, xt_family(par));
++      if (!other_dst) {
++              dst_release(this_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;
++
++      dir = CTINFO2DIR(ctinfo);
++
++      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;
++
++      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;
++
++      flow_offload_route_init(flow, &route);
++
++      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));
++
++      __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
++      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]);
++
++      return XT_CONTINUE;
++
++err_flow_add:
++      flow_offload_free(flow);
++err_flow_alloc:
++      dst_release(route.tuple[dir].dst);
++      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,
++                                 struct flow_offload *flow,
++                                 enum flow_offload_tuple_dir dir,
++                                 struct nf_flow_rule *flow_rule)
++{
++      const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
++      int err;
++
++      switch (flow_tuple->l3proto) {
++      case NFPROTO_IPV4:
++              err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule);
++              break;
++      case NFPROTO_IPV6:
++              err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule);
++              break;
++      default:
++              err = -1;
++              break;
++      }
++
++      return err;
++}
++
++static struct nf_flowtable_type flowtable_inet = {
++      .family         = NFPROTO_INET,
++      .init           = nf_flow_table_init,
++      .setup          = nf_flow_table_offload_setup,
++      .action         = nf_flow_rule_route_inet,
++      .free           = nf_flow_table_free,
++      .hook           = xt_flowoffload_net_hook,
++      .owner          = THIS_MODULE,
++};
++
++static int init_flowtable(struct xt_flowoffload_table *tbl)
++{
++      INIT_DELAYED_WORK(&tbl->work, xt_flowoffload_hook_work);
++      tbl->ft.type = &flowtable_inet;
++      tbl->ft.flags = NF_FLOWTABLE_COUNTER;
++
++      return nf_flow_table_init(&tbl->ft);
++}
++
++static int __init xt_flowoffload_tg_init(void)
++{
++      int ret;
++
++      register_netdevice_notifier(&flow_offload_netdev_notifier);
++
++      ret = init_flowtable(&flowtable[0]);
++      if (ret)
++              return ret;
++
++      ret = init_flowtable(&flowtable[1]);
++      if (ret)
++              goto cleanup;
++
++      flowtable[1].ft.flags |= NF_FLOWTABLE_HW_OFFLOAD;
++
++      ret = xt_register_target(&offload_tg_reg);
++      if (ret)
++              goto cleanup2;
++
++      return 0;
++
++cleanup2:
++      nf_flow_table_free(&flowtable[1].ft);
++cleanup:
++      nf_flow_table_free(&flowtable[0].ft);
++      return ret;
++}
++
++static void __exit xt_flowoffload_tg_exit(void)
++{
++      xt_unregister_target(&offload_tg_reg);
++      unregister_netdevice_notifier(&flow_offload_netdev_notifier);
++      nf_flow_table_free(&flowtable[0].ft);
++      nf_flow_table_free(&flowtable[1].ft);
++}
++
++MODULE_LICENSE("GPL");
++module_init(xt_flowoffload_tg_init);
++module_exit(xt_flowoffload_tg_exit);
+--- a/net/netfilter/nf_flow_table_core.c
++++ b/net/netfilter/nf_flow_table_core.c
+@@ -7,7 +7,6 @@
+ #include <linux/netdevice.h>
+ #include <net/ip.h>
+ #include <net/ip6_route.h>
+-#include <net/netfilter/nf_tables.h>
+ #include <net/netfilter/nf_flow_table.h>
+ #include <net/netfilter/nf_conntrack.h>
+ #include <net/netfilter/nf_conntrack_core.h>
+@@ -377,8 +376,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)
+@@ -439,6 +437,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
+@@ -293,6 +293,11 @@ void nf_flow_table_free(struct nf_flowta
+ void flow_offload_teardown(struct flow_offload *flow);
++int nf_flow_table_iterate(struct nf_flowtable *flow_table,
++                      void (*iter)(struct nf_flowtable *flowtable,
++                                   struct flow_offload *flow, void *data),
++                      void *data);
++
+ void nf_flow_snat_port(const struct flow_offload *flow,
+                      struct sk_buff *skb, unsigned int thoff,
+                      u8 protocol, enum flow_offload_tuple_dir dir);
diff --git a/target/linux/generic/hack-6.6/651-wireless_mesh_header.patch b/target/linux/generic/hack-6.6/651-wireless_mesh_header.patch
new file mode 100644 (file)
index 0000000..3a2a997
--- /dev/null
@@ -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
+@@ -157,8 +157,8 @@ static inline bool dev_xmit_complete(int
+ #if defined(CONFIG_HYPERV_NET)
+ # define LL_MAX_HEADER 128
+-#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25)
+-# if defined(CONFIG_MAC80211_MESH)
++#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) || 1
++# if defined(CONFIG_MAC80211_MESH) || 1
+ #  define LL_MAX_HEADER 128
+ # else
+ #  define LL_MAX_HEADER 96
diff --git a/target/linux/generic/hack-6.6/660-fq_codel_defaults.patch b/target/linux/generic/hack-6.6/660-fq_codel_defaults.patch
new file mode 100644 (file)
index 0000000..b923a2d
--- /dev/null
@@ -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
+@@ -471,7 +471,11 @@ static int fq_codel_init(struct Qdisc *s
+       sch->limit = 10*1024;
+       q->flows_cnt = 1024;
++#ifdef CONFIG_X86_64
+       q->memory_limit = 32 << 20; /* 32 MBytes */
++#else
++      q->memory_limit = 4 << 20; /* 4 MBytes */
++#endif
+       q->drop_batch_size = 64;
+       q->quantum = psched_mtu(qdisc_dev(sch));
+       INIT_LIST_HEAD(&q->new_flows);
diff --git a/target/linux/generic/hack-6.6/661-kernel-ct-size-the-hashtable-more-adequately.patch b/target/linux/generic/hack-6.6/661-kernel-ct-size-the-hashtable-more-adequately.patch
new file mode 100644 (file)
index 0000000..020f3f3
--- /dev/null
@@ -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
+@@ -2682,7 +2682,7 @@ int nf_conntrack_init_start(void)
+       if (!nf_conntrack_htable_size) {
+               nf_conntrack_htable_size
+-                      = (((nr_pages << PAGE_SHIFT) / 16384)
++                      = (((nr_pages << PAGE_SHIFT) / 2048)
+                          / sizeof(struct hlist_head));
+               if (BITS_PER_LONG >= 64 &&
+                   nr_pages > (4 * (1024 * 1024 * 1024 / PAGE_SIZE)))
diff --git a/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch
new file mode 100644 (file)
index 0000000..4428ebb
--- /dev/null
@@ -0,0 +1,131 @@
+From 36e516290611e613aa92996cb4339561452695b4 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 7 Jul 2017 17:24:23 +0200
+Subject: net: swconfig: adds openwrt switch layer
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/net/phy/Kconfig   | 83 +++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/net/phy/Makefile  | 15 +++++++++
+ include/uapi/linux/Kbuild |  1 +
+ 3 files changed, 99 insertions(+)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -66,6 +66,80 @@ config SFP
+       depends on HWMON || HWMON=n
+       select MDIO_I2C
++comment "Switch configuration API + drivers"
++
++config SWCONFIG
++      tristate "Switch configuration API"
++      help
++        Switch configuration API using netlink. This allows
++        you to configure the VLAN features of certain switches.
++
++config SWCONFIG_LEDS
++      bool "Switch LED trigger support"
++      depends on (SWCONFIG && LEDS_TRIGGERS)
++
++config ADM6996_PHY
++      tristate "Driver for ADM6996 switches"
++      select SWCONFIG
++      help
++        Currently supports the ADM6996FC and ADM6996M switches.
++        Support for FC is very limited.
++
++config AR8216_PHY
++      tristate "Driver for Atheros AR8216/8327 switches"
++      select SWCONFIG
++      select ETHERNET_PACKET_MANGLE
++
++config AR8216_PHY_LEDS
++      bool "Atheros AR8216 switch LED support"
++      depends on (AR8216_PHY && LEDS_CLASS)
++
++source "drivers/net/phy/b53/Kconfig"
++
++config IP17XX_PHY
++      tristate "Driver for IC+ IP17xx switches"
++      select SWCONFIG
++
++config PSB6970_PHY
++      tristate "Lantiq XWAY Tantos (PSB6970) Ethernet switch"
++      select SWCONFIG
++
++config RTL8306_PHY
++      tristate "Driver for Realtek RTL8306S switches"
++      select SWCONFIG
++
++config RTL8366_SMI
++      tristate "Driver for the RTL8366 SMI interface"
++      depends on GPIOLIB
++      help
++        This module implements the SMI interface protocol which is used
++        by some RTL8366 ethernet switch devices via the generic GPIO API.
++
++if RTL8366_SMI
++
++config RTL8366_SMI_DEBUG_FS
++      bool "RTL8366 SMI interface debugfs support"
++        depends on DEBUG_FS
++        default n
++
++config RTL8366S_PHY
++      tristate "Driver for the Realtek RTL8366S switch"
++      select SWCONFIG
++
++config RTL8366RB_PHY
++      tristate "Driver for the Realtek RTL8366RB switch"
++      select SWCONFIG
++
++config RTL8367_PHY
++      tristate "Driver for the Realtek RTL8367R/M switches"
++      select SWCONFIG
++
++config RTL8367B_PHY
++      tristate "Driver fot the Realtek RTL8367R-VB switch"
++      select SWCONFIG
++
++endif # RTL8366_SMI
++
+ comment "MII PHY device drivers"
+ config AIR_EN8811H_PHY
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -26,6 +26,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY)    += phy_
+ obj-$(CONFIG_PHYLINK)         += phylink.o
+ obj-$(CONFIG_PHYLIB)          += libphy.o
++obj-$(CONFIG_SWCONFIG)                += swconfig.o
++obj-$(CONFIG_ADM6996_PHY)     += adm6996.o
++obj-$(CONFIG_AR8216_PHY)      += ar8xxx.o
++ar8xxx-y                      += ar8216.o
++ar8xxx-y                      += ar8327.o
++obj-$(CONFIG_SWCONFIG_B53)    += b53/
++obj-$(CONFIG_IP17XX_PHY)      += ip17xx.o
++obj-$(CONFIG_PSB6970_PHY)     += psb6970.o
++obj-$(CONFIG_RTL8306_PHY)     += rtl8306.o
++obj-$(CONFIG_RTL8366_SMI)     += rtl8366_smi.o
++obj-$(CONFIG_RTL8366S_PHY)    += rtl8366s.o
++obj-$(CONFIG_RTL8366RB_PHY)   += rtl8366rb.o
++obj-$(CONFIG_RTL8367_PHY)     += rtl8367.o
++obj-$(CONFIG_RTL8367B_PHY)    += rtl8367b.o
++
+ obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o
+ obj-$(CONFIG_SFP)             += sfp.o
+--- a/include/linux/platform_data/b53.h
++++ b/include/linux/platform_data/b53.h
+@@ -29,6 +29,9 @@ struct b53_platform_data {
+       u32 chip_id;
+       u16 enabled_ports;
++      /* allow to specify an ethX alias */
++      const char *alias;
++
+       /* only used by MMAP'd driver */
+       unsigned big_endian:1;
+       void __iomem *regs;
diff --git a/target/linux/generic/hack-6.6/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/target/linux/generic/hack-6.6/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch
new file mode 100644 (file)
index 0000000..bf93a87
--- /dev/null
@@ -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
+@@ -3405,6 +3405,9 @@ static int mv88e6xxx_setup_port(struct m
+       else
+               reg = 1 << port;
++      /* Disable ATU member violation interrupt */
++      reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG;
++
+       err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR,
+                                  reg);
+       if (err)
diff --git a/target/linux/generic/hack-6.6/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-6.6/721-net-add-packet-mangeling.patch
new file mode 100644 (file)
index 0000000..e1d4367
--- /dev/null
@@ -0,0 +1,167 @@
+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
+@@ -1758,6 +1758,7 @@ enum netdev_priv_flags {
+       IFF_TX_SKB_NO_LINEAR            = BIT_ULL(31),
+       IFF_CHANGE_PROTO_DOWN           = BIT_ULL(32),
+       IFF_SEE_ALL_HWTSTAMP_REQUESTS   = BIT_ULL(33),
++      IFF_NO_IP_ALIGN                 = BIT_ULL(34),
+ };
+ #define IFF_802_1Q_VLAN                       IFF_802_1Q_VLAN
+@@ -1791,6 +1792,7 @@ enum netdev_priv_flags {
+ #define IFF_FAILOVER_SLAVE            IFF_FAILOVER_SLAVE
+ #define IFF_L3MDEV_RX_HANDLER         IFF_L3MDEV_RX_HANDLER
+ #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 {
+@@ -2183,6 +2185,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;
+@@ -2256,6 +2263,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
+@@ -3095,6 +3095,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
+@@ -3260,16 +3264,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
+@@ -3597,6 +3597,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;
+       trace_net_dev_start_xmit(skb, dev);
+       rc = netdev_start_xmit(skb, dev, txq, more);
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -62,6 +62,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>
+@@ -844,6 +845,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->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
+@@ -159,6 +159,12 @@ __be16 eth_type_trans(struct sk_buff *sk
+       const struct ethhdr *eth;
+       skb->dev = dev;
++
++#ifdef CONFIG_ETHERNET_PACKET_MANGLE
++      if (dev->eth_mangle_rx)
++              dev->eth_mangle_rx(dev, skb);
++#endif
++
+       skb_reset_mac_header(skb);
+       eth = (struct ethhdr *)skb->data;
diff --git a/target/linux/generic/hack-6.6/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch b/target/linux/generic/hack-6.6/722-net-phy-aquantia-enable-AQR112-and-AQR412.patch
new file mode 100644 (file)
index 0000000..522dbaf
--- /dev/null
@@ -0,0 +1,117 @@
+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/aquantia_main.c | 88 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 88 insertions(+)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -96,6 +96,29 @@
+ #define AQR107_OP_IN_PROG_SLEEP               1000
+ #define AQR107_OP_IN_PROG_TIMEOUT     100000
++/* registers in MDIO_MMD_VEND1 region */
++#define AQUANTIA_VND1_GLOBAL_SC                       0x000
++#define  AQUANTIA_VND1_GLOBAL_SC_LP           BIT(0xb)
++
++/* global start rate, the protocol associated with this speed is used by default
++ * on SI.
++ */
++#define AQUANTIA_VND1_GSTART_RATE             0x31a
++#define  AQUANTIA_VND1_GSTART_RATE_OFF                0
++#define  AQUANTIA_VND1_GSTART_RATE_100M               1
++#define  AQUANTIA_VND1_GSTART_RATE_1G         2
++#define  AQUANTIA_VND1_GSTART_RATE_10G                3
++#define  AQUANTIA_VND1_GSTART_RATE_2_5G               4
++#define  AQUANTIA_VND1_GSTART_RATE_5G         5
++
++/* SYSCFG registers for 100M, 1G, 2.5G, 5G, 10G */
++#define AQUANTIA_VND1_GSYSCFG_BASE            0x31b
++#define AQUANTIA_VND1_GSYSCFG_100M            0
++#define AQUANTIA_VND1_GSYSCFG_1G              1
++#define AQUANTIA_VND1_GSYSCFG_2_5G            2
++#define AQUANTIA_VND1_GSYSCFG_5G              3
++#define AQUANTIA_VND1_GSYSCFG_10G             4
++
+ static int aqr107_get_sset_count(struct phy_device *phydev)
+ {
+       return AQR107_SGMII_STAT_SZ;
+@@ -202,6 +225,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;
+@@ -854,7 +922,7 @@ static struct phy_driver aqr_driver[] =
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR112),
+       .name           = "Aquantia AQR112",
+       .probe          = aqr107_probe,
+-      .config_aneg    = aqr_config_aneg,
++      .config_aneg    = aqr_config_aneg_set_prot,
+       .config_intr    = aqr_config_intr,
+       .handle_interrupt = aqr_handle_interrupt,
+       .get_tunable    = aqr107_get_tunable,
+@@ -877,7 +945,7 @@ static struct phy_driver aqr_driver[] =
+       PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
+       .name           = "Aquantia AQR412",
+       .probe          = aqr107_probe,
+-      .config_aneg    = aqr_config_aneg,
++      .config_aneg    = aqr_config_aneg_set_prot,
+       .config_intr    = aqr_config_intr,
+       .handle_interrupt = aqr_handle_interrupt,
+       .get_tunable    = aqr107_get_tunable,
diff --git a/target/linux/generic/hack-6.6/723-net-phy-aquantia-fix-system-side-protocol-mi.patch b/target/linux/generic/hack-6.6/723-net-phy-aquantia-fix-system-side-protocol-mi.patch
new file mode 100644 (file)
index 0000000..b0cd801
--- /dev/null
@@ -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/aquantia_main.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -258,10 +258,16 @@ static int aqr_config_aneg_set_prot(stru
+       phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GSTART_RATE,
+                     aquantia_syscfg[if_type].start_rate);
+-      for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++)
++      for (i = 0; i <= aquantia_syscfg[if_type].cnt; i++) {
++              u16 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++                                     AQUANTIA_VND1_GSYSCFG_BASE + i);
++              if (!reg)
++                      continue;
++
+               phy_write_mmd(phydev, MDIO_MMD_VEND1,
+                             AQUANTIA_VND1_GSYSCFG_BASE + i,
+                             aquantia_syscfg[if_type].syscfg);
++      }
+       /* wake PHY back up */
+       phy_write_mmd(phydev, MDIO_MMD_VEND1, AQUANTIA_VND1_GLOBAL_SC, 0);
diff --git a/target/linux/generic/hack-6.6/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch b/target/linux/generic/hack-6.6/725-net-phy-aquantia-add-PHY_IDs-for-AQR112-variants.patch
new file mode 100644 (file)
index 0000000..ed47187
--- /dev/null
@@ -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/aquantia_main.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/phy/aquantia/aquantia_main.c
++++ b/drivers/net/phy/aquantia/aquantia_main.c
+@@ -31,6 +31,8 @@
+ #define PHY_ID_AQR113C        0x31c31c12
+ #define PHY_ID_AQR114C        0x31c31c22
+ #define PHY_ID_AQR813 0x31c31cb2
++#define PHY_ID_AQR112C        0x03a1b790
++#define PHY_ID_AQR112R        0x31c31d12
+ #define MDIO_PHYXS_VEND_IF_STATUS             0xe812
+ #define MDIO_PHYXS_VEND_IF_STATUS_TYPE_MASK   GENMASK(7, 3)
+@@ -1061,6 +1063,30 @@ static struct phy_driver aqr_driver[] =
+       .led_hw_control_get = aqr_phy_led_hw_control_get,
+       .led_polarity_set = aqr_phy_led_polarity_set,
+ },
++{
++      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,
++},
+ };
+ module_phy_driver(aqr_driver);
+@@ -1081,6 +1107,8 @@ static struct mdio_device_id __maybe_unu
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR113C) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR114C) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_AQR813) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR112C) },
++      { PHY_ID_MATCH_MODEL(PHY_ID_AQR112R) },
+       { }
+ };
diff --git a/target/linux/generic/hack-6.6/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch b/target/linux/generic/hack-6.6/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch
new file mode 100644 (file)
index 0000000..30a502a
--- /dev/null
@@ -0,0 +1,64 @@
+From 880d1311335120f64447ca9d11933872d734e19a Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 27 Mar 2023 18:41:54 +0100
+Subject: [PATCH] generic: pcs-mtk-lynxi: add hack to use 2500Base-X without AN
+
+Using 2500Base-T SFP modules e.g. on the BananaPi R3 requires manually
+disabling auto-negotiation, e.g. using ethtool. While a proper fix
+using SFP quirks is being discussed upstream, bring a work-around to
+restore user experience to what it was before the switch to the
+dedicated SGMII PCS driver.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+
+--- a/drivers/net/pcs/pcs-mtk-lynxi.c
++++ b/drivers/net/pcs/pcs-mtk-lynxi.c
+@@ -114,14 +114,23 @@ static void mtk_pcs_lynxi_get_state(stru
+                                   struct phylink_link_state *state)
+ {
+       struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
+-      unsigned int bm, adv;
++      unsigned int bm, bmsr, adv;
+       /* Read the BMSR and LPA */
+       regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
+-      regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
++      bmsr = FIELD_GET(SGMII_BMSR, bm);
++
++      if (state->interface == PHY_INTERFACE_MODE_2500BASEX) {
++              state->link = !!(bmsr & BMSR_LSTATUS);
++              state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
++              state->speed = SPEED_2500;
++              state->duplex = DUPLEX_FULL;
++
++              return;
++      }
+-      phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
+-                                       FIELD_GET(SGMII_LPA, adv));
++      regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
++      phylink_mii_c22_pcs_decode_state(state, bmsr, FIELD_GET(SGMII_LPA, adv));
+ }
+ static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs)
+@@ -142,7 +151,7 @@ static int mtk_pcs_lynxi_config(struct p
+ {
+       struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
+       bool mode_changed = false, changed;
+-      unsigned int rgc3, sgm_mode, bmcr;
++      unsigned int rgc3, sgm_mode, bmcr = 0;
+       int advertise, link_timer;
+       advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
+@@ -165,9 +174,8 @@ static int mtk_pcs_lynxi_config(struct p
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
+               if (interface == PHY_INTERFACE_MODE_SGMII)
+                       sgm_mode |= SGMII_SPEED_DUPLEX_AN;
+-              bmcr = BMCR_ANENABLE;
+-      } else {
+-              bmcr = 0;
++              if (interface != PHY_INTERFACE_MODE_2500BASEX)
++                      bmcr = BMCR_ANENABLE;
+       }
+       if (mpcs->interface != interface) {
diff --git a/target/linux/generic/hack-6.6/760-net-usb-r8152-add-LED-configuration-from-OF.patch b/target/linux/generic/hack-6.6/760-net-usb-r8152-add-LED-configuration-from-OF.patch
new file mode 100644 (file)
index 0000000..069c682
--- /dev/null
@@ -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>
+@@ -7044,6 +7045,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;
+@@ -7085,6 +7102,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)
+@@ -7225,6 +7244,8 @@ static void r8153_init(struct r8152 *tp)
+               tp->coalesce = COALESCE_SLOW;
+               break;
+       }
++
++      r8152_led_configuration(tp);
+ }
+ static void r8153b_init(struct r8152 *tp)
+@@ -7307,6 +7328,8 @@ static void r8153b_init(struct r8152 *tp
+       rtl_tally_reset(tp);
+       tp->coalesce = 15000;   /* 15 us */
++
++      r8152_led_configuration(tp);
+ }
+ static void r8153c_init(struct r8152 *tp)
diff --git a/target/linux/generic/hack-6.6/761-dt-bindings-net-add-RTL8152-binding-documentation.patch b/target/linux/generic/hack-6.6/761-dt-bindings-net-add-RTL8152-binding-documentation.patch
new file mode 100644 (file)
index 0000000..be262b9
--- /dev/null
@@ -0,0 +1,54 @@
+From 3ee05f4aa64fc86af3be5bc176ba5808de9260a7 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sun, 26 Jul 2020 15:30:33 +0200
+Subject: [PATCH] dt-bindings: net: add RTL8152 binding documentation
+
+Add binding documentation for the Realtek RTL8152 / RTL8153 USB ethernet
+adapters.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ .../bindings/net/realtek,rtl8152.yaml         | 36 +++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/realtek,rtl8152.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/realtek,rtl8152.yaml
+@@ -0,0 +1,36 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/realtek,rtl8152.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Realtek RTL8152/RTL8153 series USB ethernet
++
++maintainers:
++  - David Bauer <mail@david-bauer.net>
++
++properties:
++  compatible:
++    oneOf:
++      - items:
++          - enum:
++              - realtek,rtl8152
++              - realtek,rtl8153
++
++  reg:
++    description: The device number on the USB bus
++
++  realtek,led-data:
++    description: Value to be written to the LED configuration register.
++
++required:
++  - compatible
++  - reg
++
++examples:
++  - |
++    usb-eth@2 {
++      compatible = "realtek,rtl8153";
++      reg = <2>;
++      realtek,led-data = <0x87>;
++    };
+\ No newline at end of file
diff --git a/target/linux/generic/hack-6.6/765-mxl-gpy-control-LED-reg-from-DT.patch b/target/linux/generic/hack-6.6/765-mxl-gpy-control-LED-reg-from-DT.patch
new file mode 100644 (file)
index 0000000..041f05e
--- /dev/null
@@ -0,0 +1,74 @@
+From 94b90966095f3fa625897e8f53d215882f6e19b3 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sat, 11 Mar 2023 17:00:01 +0100
+Subject: [PATCH] mxl-gpy: control LED reg from DT
+
+Add dynamic configuration for the LED control registers on MXL PHYs.
+
+This patch has been tested with MaxLinear GPY211C. It is unlikely to be
+accepted upstream, as upstream plans on integrating their own framework
+for handling these LEDs.
+
+For the time being, use this hack to configure PHY driven device-LEDs to
+show the correct state.
+
+A possible alternative might be to expose the LEDs using the kernel LED
+framework and bind it to the netdevice. This might also be upstreamable,
+although it is a considerable extra amount of work.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/mxl-gpy.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 36 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/mxl-gpy.c
++++ b/drivers/net/phy/mxl-gpy.c
+@@ -10,6 +10,7 @@
+ #include <linux/bitfield.h>
+ #include <linux/hwmon.h>
+ #include <linux/mutex.h>
++#include <linux/of.h>
+ #include <linux/phy.h>
+ #include <linux/polynomial.h>
+ #include <linux/property.h>
+@@ -293,10 +294,39 @@ out:
+       return ret;
+ }
++static int gpy_led_write(struct phy_device *phydev)
++{
++      struct device_node *node = phydev->mdio.dev.of_node;
++      u32 led_regs[GPY_MAX_LEDS];
++      int i, ret;
++      u16 val = 0xff00;
++
++      if (!IS_ENABLED(CONFIG_OF_MDIO))
++              return 0;
++
++      if (of_property_read_u32_array(node, "mxl,led-config", led_regs, GPY_MAX_LEDS))
++              return 0;
++
++      if (of_property_read_bool(node, "mxl,led-drive-vdd"))
++              val &= 0x0fff;
++
++      /* Enable LED function handling on all ports*/
++      phy_write(phydev, PHY_LED, val);
++
++      /* Write LED register values */
++      for (i = 0; i < GPY_MAX_LEDS; i++) {
++              ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(i), (u16)led_regs[i]);
++              if (ret < 0)
++                      return ret;
++      }
++
++      return 0;
++}
++
+ static int gpy_config_init(struct phy_device *phydev)
+ {
+       /* Nothing to configure. Configuration Requirement Placeholder */
+-      return 0;
++      return gpy_led_write(phydev);
+ }
+ static int gpy_probe(struct phy_device *phydev)
diff --git a/target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch b/target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch
new file mode 100644 (file)
index 0000000..3405d5c
--- /dev/null
@@ -0,0 +1,72 @@
+From cc225d163b5a4f7a0d1968298bf7927306646a47 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Fri, 28 Apr 2023 01:53:01 +0200
+Subject: [PATCH] net: phy: mediatek-ge: add LED configuration interface
+
+This adds a small hack similar to the one used for ar8xxx switches to
+read a reg:value map for configuring the LED configuration registers.
+
+This allows OpenWrt to write device-specific LED action as well as blink
+configurations. It is unlikely to be accepted upstream, as upstream
+plans on integrating their own framework for handling these LEDs.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/mediatek-ge.c | 33 +++++++++++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+--- a/drivers/net/phy/mediatek-ge.c
++++ b/drivers/net/phy/mediatek-ge.c
+@@ -1,4 +1,5 @@
+ // SPDX-License-Identifier: GPL-2.0+
++#include <linux/of.h>
+ #include <linux/bitfield.h>
+ #include <linux/module.h>
+ #include <linux/phy.h>
+@@ -53,6 +54,36 @@ static int mt7530_phy_config_init(struct
+       return 0;
+ }
++static int mt7530_led_config_of(struct phy_device *phydev)
++{
++      struct device_node *np = phydev->mdio.dev.of_node;
++      const __be32 *paddr;
++      int len;
++      int i;
++
++      paddr = of_get_property(np, "mediatek,led-config", &len);
++      if (!paddr)
++              return 0;
++
++      if (len < (2 * sizeof(*paddr)))
++              return -EINVAL;
++
++      len /= sizeof(*paddr);
++
++      phydev_warn(phydev, "Configure LED registers (num=%d)\n", len);
++      for (i = 0; i < len - 1; i += 2) {
++              u32 reg;
++              u32 val;
++
++              reg = be32_to_cpup(paddr + i);
++              val = be32_to_cpup(paddr + i + 1);
++
++              phy_write_mmd(phydev, MDIO_MMD_VEND2, reg, val);
++      }
++
++      return 0;
++}
++
+ static int mt7531_phy_config_init(struct phy_device *phydev)
+ {
+       mtk_gephy_config_init(phydev);
+@@ -65,6 +96,9 @@ static int mt7531_phy_config_init(struct
+       phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404);
+       phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404);
++      /* LED Config*/
++      mt7530_led_config_of(phydev);
++
+       return 0;
+ }
diff --git a/target/linux/generic/hack-6.6/773-bgmac-add-srab-switch.patch b/target/linux/generic/hack-6.6/773-bgmac-add-srab-switch.patch
new file mode 100644 (file)
index 0000000..633cacd
--- /dev/null
@@ -0,0 +1,98 @@
+From 3cb240533ab787899dc7f17aa7d6c5b4810e2e58 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Fri, 7 Jul 2017 17:26:01 +0200
+Subject: bcm53xx: bgmac: use srab switch driver
+
+use the srab switch driver on these SoCs.
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma.c |  1 +
+ drivers/net/ethernet/broadcom/bgmac.c      | 24 ++++++++++++++++++++++++
+ drivers/net/ethernet/broadcom/bgmac.h      |  4 ++++
+ 3 files changed, 29 insertions(+)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -280,6 +280,7 @@ static int bgmac_probe(struct bcma_devic
+               bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST;
+               bgmac->feature_flags |= BGMAC_FEAT_NO_RESET;
+               bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500;
++              bgmac->feature_flags |= BGMAC_FEAT_SRAB;
+               break;
+       default:
+               bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST;
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -12,6 +12,7 @@
+ #include <linux/bcma/bcma.h>
+ #include <linux/etherdevice.h>
+ #include <linux/interrupt.h>
++#include <linux/platform_data/b53.h>
+ #include <linux/bcm47xx_nvram.h>
+ #include <linux/phy.h>
+ #include <linux/phy_fixed.h>
+@@ -1408,6 +1409,17 @@ static const struct ethtool_ops bgmac_et
+       .set_link_ksettings     = phy_ethtool_set_link_ksettings,
+ };
++static struct b53_platform_data bgmac_b53_pdata = {
++};
++
++static struct platform_device bgmac_b53_dev = {
++      .name           = "b53-srab-switch",
++      .id             = -1,
++      .dev            = {
++              .platform_data = &bgmac_b53_pdata,
++      },
++};
++
+ /**************************************************
+  * MII
+  **************************************************/
+@@ -1546,6 +1558,14 @@ int bgmac_enet_probe(struct bgmac *bgmac
+       bgmac->in_init = false;
++      if ((bgmac->feature_flags & BGMAC_FEAT_SRAB) && !bgmac_b53_pdata.regs) {
++              bgmac_b53_pdata.regs = ioremap(0x18007000, 0x1000);
++
++              err = platform_device_register(&bgmac_b53_dev);
++              if (!err)
++                      bgmac->b53_device = &bgmac_b53_dev;
++      }
++
+       err = register_netdev(bgmac->net_dev);
+       if (err) {
+               dev_err(bgmac->dev, "Cannot register net device\n");
+@@ -1568,6 +1588,10 @@ EXPORT_SYMBOL_GPL(bgmac_enet_probe);
+ void bgmac_enet_remove(struct bgmac *bgmac)
+ {
++      if (bgmac->b53_device)
++              platform_device_unregister(&bgmac_b53_dev);
++      bgmac->b53_device = NULL;
++
+       unregister_netdev(bgmac->net_dev);
+       phy_disconnect(bgmac->net_dev->phydev);
+       netif_napi_del(&bgmac->napi);
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -388,6 +388,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/target/linux/generic/hack-6.6/780-usb-net-MeigLink_modem_support.patch b/target/linux/generic/hack-6.6/780-usb-net-MeigLink_modem_support.patch
new file mode 100644 (file)
index 0000000..cefb9c5
--- /dev/null
@@ -0,0 +1,70 @@
+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
+@@ -1084,6 +1084,11 @@ static const struct usb_device_id produc
+               USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7),
+               .driver_info = (unsigned long)&qmi_wwan_info,
+       },
++      {       /* Meiglink SGM828 */
++              USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d49, USB_CLASS_VENDOR_SPEC, 0x10, 0x05),
++              .driver_info = (unsigned long)&qmi_wwan_info,
++      },
++
+       {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0122)},   /* Quectel RG650V */
+       {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)},   /* Quectel EC25, EC20 R2.0  Mini PCIe */
+       {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)},   /* Quectel EP06/EG06/EM06 */
+@@ -1091,6 +1096,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
+@@ -247,6 +247,11 @@ static void option_instat_callback(struc
+ #define UBLOX_PRODUCT_R410M                   0x90b2
+ /* These Yuga products use Qualcomm's vendor ID */
+ #define YUGA_PRODUCT_CLM920_NC5                       0x9625
++/* These MeigLink products use Qualcomm's vendor ID */
++#define MEIGLINK_PRODUCT_SLM750                       0xf601
++
++#define MEIGLINK_VENDOR_ID                    0x2dee
++#define MEIGLINK_PRODUCT_SLM828                       0x4d49
+ #define QUECTEL_VENDOR_ID                     0x2c7c
+ /* These Quectel products use Quectel's vendor ID */
+@@ -1160,6 +1165,11 @@ static const struct usb_device_id option
+       { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */
+       { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000), /* SIMCom SIM5218 */
+         .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) | NCTRL(3) | RSVD(4) },
++      /* MeiG */
++      { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x01) },
++      { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x02) },
++      { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x03) },
++      { USB_DEVICE_AND_INTERFACE_INFO(MEIGLINK_VENDOR_ID, MEIGLINK_PRODUCT_SLM828, USB_CLASS_VENDOR_SPEC, 0x10, 0x04) },
+       /* Quectel products using Qualcomm vendor ID */
+       { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)},
+       { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20),
+@@ -1201,6 +1211,11 @@ static const struct usb_device_id option
+         .driver_info = ZLP },
+       { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
+         .driver_info = RSVD(4) },
++      /* Meiglink products using Qualcomm vendor ID */
++      // Works OK. In case of some issues check macros that are used by Quectel Products
++      { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0xff, 0xff),
++        .driver_info = NUMEP2 },
++      { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750, 0xff, 0, 0) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
+         .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },
diff --git a/target/linux/generic/hack-6.6/781-usb-net-rndis-support-asr.patch b/target/linux/generic/hack-6.6/781-usb-net-rndis-support-asr.patch
new file mode 100644 (file)
index 0000000..47339b6
--- /dev/null
@@ -0,0 +1,69 @@
+From 9fabf60187f1fa19e6f6bb5441587d485bd534b0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 9 Apr 2024 17:06:38 +0100
+Subject: [PATCH] rndis_host: add a bunch of USB IDs
+
+Add a bunch of USB IDs found in various places online to the
+RNDIS USB network driver.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/usb/rndis_host.c | 40 ++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+--- a/drivers/net/usb/rndis_host.c
++++ b/drivers/net/usb/rndis_host.c
+@@ -630,6 +630,16 @@ static const struct driver_info   zte_rndi
+       .tx_fixup =     rndis_tx_fixup,
+ };
++static const struct driver_info asr_rndis_info = {
++      .description =  "Asr RNDIS device",
++      .flags =        FLAG_WWAN | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT | FLAG_NOARP,
++      .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 [] = {
+@@ -666,6 +676,36 @@ static const struct usb_device_id produc
+       USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
+       .driver_info = (unsigned long) &rndis_info,
+ }, {
++      /* Quectel EG060V rndis device */
++      USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x6004,
++                                    USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
++      .driver_info = (unsigned long) &asr_rndis_info,
++}, {
++      /* Quectel EC200A rndis device */
++      USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x6005,
++                                    USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
++      .driver_info = (unsigned long) &asr_rndis_info,
++}, {
++      /* Quectel EC200T rndis device */
++      USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x6026,
++                                    USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
++      .driver_info = (unsigned long) &asr_rndis_info,
++}, {
++      /* Simcom A7906E rndis device */
++      USB_DEVICE_AND_INTERFACE_INFO(0x1e0e, 0x9011,
++                                    USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
++      .driver_info = (unsigned long) &asr_rndis_info,
++}, {
++      /* Meig SLM770A */
++      USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d57,
++                                    USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
++      .driver_info = (unsigned long) &asr_rndis_info,
++}, {
++      /* Meig SLM828 */
++      USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d49,
++                                    USB_CLASS_WIRELESS_CONTROLLER, 1, 3),
++      .driver_info = (unsigned long) &asr_rndis_info,
++}, {
+       /* Novatel Verizon USB730L */
+       USB_INTERFACE_INFO(USB_CLASS_MISC, 4, 1),
+       .driver_info = (unsigned long) &rndis_info,
diff --git a/target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch b/target/linux/generic/hack-6.6/790-SFP-GE-T-ignore-TX_FAULT.patch
new file mode 100644 (file)
index 0000000..7733b45
--- /dev/null
@@ -0,0 +1,63 @@
+From 7cc39a6bedbd85f3ff7e16845f310e4ce8d9833f Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 6 Sep 2022 00:31:19 +0100
+Subject: [PATCH] net: sfp: add quirk for ATS SFP-GE-T 1000Base-TX module
+To: netdev@vger.kernel.org,
+    linux-kernel@vger.kernel.org,
+    Russell King <linux@armlinux.org.uk>,
+    Andrew Lunn <andrew@lunn.ch>,
+    Heiner Kallweit <hkallweit1@gmail.com>
+Cc: David S. Miller <davem@davemloft.net>,
+    Eric Dumazet <edumazet@google.com>,
+    Jakub Kicinski <kuba@kernel.org>,
+    Paolo Abeni <pabeni@redhat.com>,
+    Josef Schlehofer <pepe.schlehofer@gmail.com>
+
+This copper module comes with broken TX_FAULT indicator which must be
+ignored for it to work. Implement ignoring TX_FAULT state bit also
+during reset/insertion and mute the warning telling the user that the
+module indicates TX_FAULT.
+
+Co-authored-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/sfp.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -471,6 +471,9 @@ static const struct sfp_quirk sfp_quirks
+       // FS 2.5G Base-T
+       SFP_QUIRK_M("FS", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
++      // OEM SFP-GE-T is 1000Base-T module
++      SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault),
++
+       // Lantech 8330-262D-E can operate at 2500base-X, but incorrectly report
+       // 2500MBd NRZ in their EEPROM
+       SFP_QUIRK_M("Lantech", "8330-262D-E", sfp_quirk_2500basex),
+@@ -2589,7 +2592,8 @@ static void sfp_sm_main(struct sfp *sfp,
+                        * or t_start_up, so assume there is a fault.
+                        */
+                       sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT,
+-                                   sfp->sm_fault_retries == N_FAULT_INIT);
++                                   !sfp->tx_fault_ignore &&
++                                   (sfp->sm_fault_retries == N_FAULT_INIT));
+               } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
+       init_done:
+                       /* Create mdiobus and start trying for PHY */
+@@ -2843,10 +2847,12 @@ static void sfp_check_state(struct sfp *
+       mutex_lock(&sfp->st_mutex);
+       state = sfp_get_state(sfp);
+       changed = state ^ sfp->state;
+-      if (sfp->tx_fault_ignore)
++      if (sfp->tx_fault_ignore) {
+               changed &= SFP_F_PRESENT | SFP_F_LOS;
+-      else
++              state &= ~SFP_F_TX_FAULT;
++      } else {
+               changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
++      }
+       for (i = 0; i < GPIO_MAX; i++)
+               if (changed & BIT(i))
diff --git a/target/linux/generic/hack-6.6/800-GPIO-add-named-gpio-exports.patch b/target/linux/generic/hack-6.6/800-GPIO-add-named-gpio-exports.patch
new file mode 100644 (file)
index 0000000..e6dcdfd
--- /dev/null
@@ -0,0 +1,172 @@
+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
+@@ -21,6 +21,8 @@
+ #include <linux/gpio/consumer.h>
+ #include <linux/gpio/machine.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
+ #include "gpiolib.h"
+ #include "gpiolib-of.h"
+@@ -1129,3 +1131,73 @@ void of_gpiochip_remove(struct gpio_chip
+ {
+       of_node_put(dev_of_node(&chip->gpiodev->dev));
+ }
++
++#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_named_count(cnp, "gpios");
++
++              for (i = 0; i < max_gpio; i++) {
++                      struct gpio_desc *desc;
++                      unsigned flags = 0;
++                      enum of_gpio_flags of_flags;
++
++                      desc = of_get_named_gpiod_flags(cnp, "gpios", i, &of_flags);
++                      if (IS_ERR(desc))
++                              return PTR_ERR(desc);
++                      gpio = desc_to_gpio(desc);
++
++                      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_to_desc(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",
++              .of_match_table = of_match_ptr(gpio_export_ids),
++      },
++      .probe          = of_gpio_export_probe,
++};
++
++module_platform_driver(gpio_export_driver);
++
++#endif
+--- a/include/linux/gpio/consumer.h
++++ b/include/linux/gpio/consumer.h
+@@ -644,7 +644,10 @@ static inline struct gpio_desc *acpi_get
+ #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 gpio_export_with_name(struct gpio_desc *desc, bool direction_may_change,
++                        const char *name);
+ int gpiod_export_link(struct device *dev, const char *name,
+                     struct gpio_desc *desc);
+ void gpiod_unexport(struct gpio_desc *desc);
+@@ -653,11 +656,25 @@ void gpiod_unexport(struct gpio_desc *de
+ #include <asm/errno.h>
++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)
+ {
+       return -ENOSYS;
+ }
++
++static inline int gpio_export_with_name(struct gpio_desc *desc,
++                                      bool direction_may_change,
++                                      const char *name)
++{
++      return -ENOSYS;
++}
+ static inline int gpiod_export_link(struct device *dev, const char *name,
+                                   struct gpio_desc *desc)
+--- a/drivers/gpio/gpiolib-sysfs.c
++++ b/drivers/gpio/gpiolib-sysfs.c
+@@ -558,7 +558,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;
+@@ -620,6 +620,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,
+@@ -641,8 +643,21 @@ 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);
++int gpio_export_with_name(struct gpio_desc *desc, bool direction_may_change,
++                        const char *name)
++{
++      return __gpiod_export(desc, direction_may_change, name);
++}
++EXPORT_SYMBOL_GPL(gpio_export_with_name);
++
+ static int match_export(struct device *dev, const void *desc)
+ {
+       struct gpiod_data *data = dev_get_drvdata(dev);
diff --git a/target/linux/generic/hack-6.6/810-bcma-ssb-fallback-sprom.patch b/target/linux/generic/hack-6.6/810-bcma-ssb-fallback-sprom.patch
new file mode 100644 (file)
index 0000000..08085d9
--- /dev/null
@@ -0,0 +1,182 @@
+From e4d708702e6c98f2111e33201a264d6788564cb2 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Fri, 12 May 2023 11:08:43 +0200
+Subject: [PATCH] ssb_sprom: add generic kernel support for Broadcom  Fallback SPROMs
+
+---
+ drivers/bcma/Kconfig        |  4 ++++
+ drivers/bcma/Makefile       |  1 +
+ drivers/bcma/bcma_private.h |  1 +
+ drivers/bcma/main.c         |  8 ++++++++
+ drivers/bcma/sprom.c        | 23 ++++++++++++++---------
+ drivers/ssb/Kconfig         |  5 +++++
+ drivers/ssb/Makefile        |  1 +
+ drivers/ssb/main.c          |  8 ++++++++
+ drivers/ssb/sprom.c         | 12 +++++++++++-
+ drivers/ssb/ssb_private.h   |  2 +-
+ 10 files changed, 54 insertions(+), 11 deletions(-)
+
+--- a/drivers/bcma/Kconfig
++++ b/drivers/bcma/Kconfig
+@@ -18,6 +18,10 @@ config BCMA_BLOCKIO
+       bool
+       default y
++config BCMA_FALLBACK_SPROM
++      bool
++      default y
++
+ config BCMA_HOST_PCI_POSSIBLE
+       bool
+       depends on PCI = y
+--- a/drivers/bcma/Makefile
++++ b/drivers/bcma/Makefile
+@@ -11,6 +11,7 @@ bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE)
+ bcma-$(CONFIG_BCMA_DRIVER_MIPS)               += driver_mips.o
+ bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN)   += driver_gmac_cmn.o
+ bcma-$(CONFIG_BCMA_DRIVER_GPIO)               += driver_gpio.o
++bcma-$(CONFIG_BCMA_FALLBACK_SPROM)    += fallback-sprom.o
+ bcma-$(CONFIG_BCMA_HOST_PCI)          += host_pci.o
+ bcma-$(CONFIG_BCMA_HOST_SOC)          += host_soc.o
+ obj-$(CONFIG_BCMA)                    += bcma.o
+--- a/drivers/bcma/bcma_private.h
++++ b/drivers/bcma/bcma_private.h
+@@ -8,6 +8,7 @@
+ #include <linux/bcma/bcma.h>
+ #include <linux/delay.h>
++#include "fallback-sprom.h"
+ #define bcma_err(bus, fmt, ...) \
+       dev_err((bus)->dev, "bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
+--- a/drivers/bcma/main.c
++++ b/drivers/bcma/main.c
+@@ -671,6 +671,14 @@ static int __init bcma_modinit(void)
+ {
+       int err;
++#ifdef CONFIG_BCMA_FALLBACK_SPROM
++      err = bcma_fbs_register();
++      if (err) {
++              pr_err("Fallback SPROM initialization failed\n");
++              err = 0;
++      }
++#endif /* CONFIG_BCMA_FALLBACK_SPROM */
++
+       err = bcma_init_bus_register();
+       if (err)
+               return err;
+--- a/drivers/bcma/sprom.c
++++ b/drivers/bcma/sprom.c
+@@ -51,21 +51,26 @@ static int bcma_fill_sprom_with_fallback
+ {
+       int err;
+-      if (!get_fallback_sprom) {
++      if (get_fallback_sprom)
++              err = get_fallback_sprom(bus, out);
++
++#ifdef CONFIG_BCMA_FALLBACK_SPROM
++      if (!get_fallback_sprom || err)
++              err = bcma_get_fallback_sprom(bus, out);
++#else
++      if (!get_fallback_sprom)
+               err = -ENOENT;
+-              goto fail;
+-      }
++#endif /* CONFIG_BCMA_FALLBACK_SPROM */
+-      err = get_fallback_sprom(bus, out);
+-      if (err)
+-              goto fail;
++      if (err) {
++              bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err);
++              return err;
++      }
+       bcma_debug(bus, "Using SPROM revision %d provided by platform.\n",
+                  bus->sprom.revision);
++
+       return 0;
+-fail:
+-      bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err);
+-      return err;
+ }
+ /**************************************************
+--- a/drivers/ssb/Kconfig
++++ b/drivers/ssb/Kconfig
+@@ -25,6 +25,11 @@ if SSB
+ config SSB_SPROM
+       bool
++config SSB_FALLBACK_SPROM
++      bool
++      depends on SSB_PCIHOST
++      default y
++
+ # Support for Block-I/O. SELECT this from the driver that needs it.
+ config SSB_BLOCKIO
+       bool
+--- a/drivers/ssb/Makefile
++++ b/drivers/ssb/Makefile
+@@ -2,6 +2,7 @@
+ # core
+ ssb-y                                 += main.o scan.o
+ ssb-$(CONFIG_SSB_EMBEDDED)            += embedded.o
++ssb-$(CONFIG_SSB_FALLBACK_SPROM)      += fallback-sprom.o
+ ssb-$(CONFIG_SSB_SPROM)                       += sprom.o
+ # host support
+--- a/drivers/ssb/main.c
++++ b/drivers/ssb/main.c
+@@ -1289,6 +1289,14 @@ static int __init ssb_modinit(void)
+ {
+       int err;
++#ifdef CONFIG_SSB_FALLBACK_SPROM
++      err = ssb_fbs_register();
++      if (err) {
++              pr_err("Fallback SPROM initialization failed\n");
++              err = 0;
++      }
++#endif /* CONFIG_SSB_FALLBACK_SPROM */
++
+       /* See the comment at the ssb_is_early_boot definition */
+       ssb_is_early_boot = 0;
+       err = bus_register(&ssb_bustype);
+--- a/drivers/ssb/sprom.c
++++ b/drivers/ssb/sprom.c
+@@ -180,10 +180,20 @@ int ssb_arch_register_fallback_sprom(int
+ int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out)
+ {
++      int err;
++
++      if (get_fallback_sprom)
++              err = get_fallback_sprom(bus, out);
++
++#ifdef CONFIG_SSB_FALLBACK_SPROM
++      if (!get_fallback_sprom || err)
++              err = ssb_get_fallback_sprom(bus, out);
++#else
+       if (!get_fallback_sprom)
+               return -ENOENT;
++#endif /* CONFIG_SSB_FALLBACK_SPROM */
+-      return get_fallback_sprom(bus, out);
++      return err;
+ }
+ /* https://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
+--- a/drivers/ssb/ssb_private.h
++++ b/drivers/ssb/ssb_private.h
+@@ -8,7 +8,7 @@
+ #include <linux/ssb/ssb.h>
+ #include <linux/types.h>
+ #include <linux/bcm47xx_wdt.h>
+-
++#include "fallback-sprom.h"
+ /* pci.c */
+ #ifdef CONFIG_SSB_PCIHOST
diff --git a/target/linux/generic/hack-6.6/901-debloat_sock_diag.patch b/target/linux/generic/hack-6.6/901-debloat_sock_diag.patch
new file mode 100644 (file)
index 0000000..4b1ea3c
--- /dev/null
@@ -0,0 +1,181 @@
+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
+@@ -129,6 +129,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
+@@ -11,12 +11,13 @@ 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 gro.o \
+                       netdev-genl.o netdev-genl-gen.o gso.o
+ obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.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
+@@ -118,6 +118,7 @@
+ #include <linux/mroute.h>
+ #include <linux/mroute6.h>
+ #include <linux/icmpv6.h>
++#include <linux/cookie.h>
+ #include <linux/uaccess.h>
+@@ -150,6 +151,7 @@
+ static DEFINE_MUTEX(proto_list_mutex);
+ static LIST_HEAD(proto_list);
++DEFINE_COOKIE(sock_cookie);
+ static void sock_def_write_space_wfree(struct sock *sk);
+ static void sock_def_write_space(struct sock *sk);
+@@ -590,6 +592,21 @@ discard_and_relse:
+ }
+ EXPORT_SYMBOL(__sk_receive_skb);
++u64 __sock_gen_cookie(struct sock *sk)
++{
++      u64 res = atomic64_read(&sk->sk_cookie);
++
++      if (!res) {
++              u64 new = gen_cookie_next(&sock_cookie);
++
++              atomic64_cmpxchg(&sk->sk_cookie, res, new);
++
++              /* Another thread might have changed sk_cookie before us. */
++              res = atomic64_read(&sk->sk_cookie);
++      }
++      return 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 *,
+@@ -2239,9 +2256,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
+@@ -12,7 +12,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>
+@@ -22,23 +21,6 @@ static const struct sock_diag_inet_compa
+ static struct workqueue_struct *broadcast_wq;
+-DEFINE_COOKIE(sock_cookie);
+-
+-u64 __sock_gen_cookie(struct sock *sk)
+-{
+-      u64 res = atomic64_read(&sk->sk_cookie);
+-
+-      if (!res) {
+-              u64 new = gen_cookie_next(&sock_cookie);
+-
+-              atomic64_cmpxchg(&sk->sk_cookie, res, new);
+-
+-              /* Another thread might have changed sk_cookie before us. */
+-              res = atomic64_read(&sk->sk_cookie);
+-      }
+-      return res;
+-}
+-
+ int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie)
+ {
+       u64 res;
+--- a/net/ipv4/Kconfig
++++ b/net/ipv4/Kconfig
+@@ -423,6 +423,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
+@@ -29,6 +29,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.
+--- a/net/xdp/Kconfig
++++ b/net/xdp/Kconfig
+@@ -10,6 +10,7 @@ config XDP_SOCKETS
+ config XDP_SOCKETS_DIAG
+       tristate "XDP sockets: monitoring interface"
+       depends on XDP_SOCKETS
++      select SOCK_DIAG
+       default n
+       help
+         Support for PF_XDP sockets monitoring interface used by the ss tool.
diff --git a/target/linux/generic/hack-6.6/902-debloat_proc.patch b/target/linux/generic/hack-6.6/902-debloat_proc.patch
new file mode 100644 (file)
index 0000000..2c3eca2
--- /dev/null
@@ -0,0 +1,419 @@
+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
+@@ -2895,6 +2895,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
+@@ -101,6 +101,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
+@@ -107,6 +107,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
+@@ -131,7 +131,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;
+@@ -148,6 +151,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;
+@@ -162,6 +168,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
+@@ -1370,6 +1370,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
+@@ -4448,6 +4448,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
+@@ -2136,10 +2136,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
+@@ -327,10 +327,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;
+@@ -340,9 +342,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;
+@@ -352,8 +356,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
+@@ -4140,6 +4140,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
+@@ -3037,11 +3037,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;
+@@ -3052,17 +3054,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
+@@ -557,5 +557,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
+@@ -381,6 +381,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);
+ }
+--- a/net/ipv4/inet_timewait_sock.c
++++ b/net/ipv4/inet_timewait_sock.c
+@@ -266,7 +266,7 @@ void __inet_twsk_schedule(struct inet_ti
+        */
+       if (!rearm) {
+-              bool kill = timeo <= 4*HZ;
++              bool __maybe_unused  kill = timeo <= 4*HZ;
+               __NET_INC_STATS(twsk_net(tw), kill ? LINUX_MIB_TIMEWAITKILLED :
+                                                    LINUX_MIB_TIMEWAITED);
diff --git a/target/linux/generic/hack-6.6/904-debloat_dma_buf.patch b/target/linux/generic/hack-6.6/904-debloat_dma_buf.patch
new file mode 100644 (file)
index 0000000..355bd0d
--- /dev/null
@@ -0,0 +1,93 @@
+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
+@@ -198,7 +198,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,12 +1,14 @@
+ # 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-fence-unwrap.o dma-resv.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) += dma-buf-sysfs-stats.o
+ dmabuf_selftests-y := \
+       selftest.o \
+@@ -15,4 +17,6 @@ dmabuf_selftests-y := \
+       st-dma-fence-unwrap.o \
+       st-dma-resv.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
+@@ -1731,4 +1731,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
+@@ -4483,6 +4483,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
+@@ -314,6 +314,7 @@ char *dynamic_dname(char *buffer, int bu
+       buffer += buflen - sz;
+       return memcpy(buffer, temp, sz);
+ }
++EXPORT_SYMBOL_GPL(dynamic_dname);
+ char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
+ {
diff --git a/target/linux/generic/hack-6.6/910-kobject_uevent.patch b/target/linux/generic/hack-6.6/910-kobject_uevent.patch
new file mode 100644 (file)
index 0000000..c4c41ca
--- /dev/null
@@ -0,0 +1,32 @@
+From 0d37e6edc09c99e683dd91ca0e83bbc0df8477b3 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 16 Jul 2017 16:56:10 +0200
+Subject: lib: add uevent_next_seqnum()
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ include/linux/kobject.h |  5 +++++
+ lib/kobject_uevent.c    | 37 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 42 insertions(+)
+
+--- a/lib/kobject_uevent.c
++++ b/lib/kobject_uevent.c
+@@ -179,6 +179,18 @@ out:
+       return r;
+ }
++u64 uevent_next_seqnum(void)
++{
++      u64 seq;
++
++      mutex_lock(&uevent_sock_mutex);
++      seq = ++uevent_seqnum;
++      mutex_unlock(&uevent_sock_mutex);
++
++      return seq;
++}
++EXPORT_SYMBOL_GPL(uevent_next_seqnum);
++
+ /**
+  * kobject_synth_uevent - send synthetic uevent with arguments
+  *
diff --git a/target/linux/generic/hack-6.6/911-kobject_add_broadcast_uevent.patch b/target/linux/generic/hack-6.6/911-kobject_add_broadcast_uevent.patch
new file mode 100644 (file)
index 0000000..372ac6f
--- /dev/null
@@ -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 broadcast_uevent()
+
+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[];
+@@ -219,4 +221,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
+@@ -706,6 +706,43 @@ int add_uevent_var(struct kobj_uevent_en
+ EXPORT_SYMBOL_GPL(add_uevent_var);
+ #if defined(CONFIG_NET)
++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group,
++                   gfp_t allocation)
++{
++      struct uevent_sock *ue_sk;
++      int err = 0;
++
++      /* send netlink message */
++      mutex_lock(&uevent_sock_mutex);
++      list_for_each_entry(ue_sk, &uevent_sock_list, list) {
++              struct sock *uevent_sock = ue_sk->sk;
++              struct sk_buff *skb2;
++
++              skb2 = skb_clone(skb, allocation);
++              if (!skb2)
++                      break;
++
++              err = netlink_broadcast(uevent_sock, skb2, pid, group,
++                                      allocation);
++              if (err)
++                      break;
++      }
++      mutex_unlock(&uevent_sock_mutex);
++
++      kfree_skb(skb);
++      return err;
++}
++#else
++int broadcast_uevent(struct sk_buff *skb, __u32 pid, __u32 group,
++                   gfp_t allocation)
++{
++      kfree_skb(skb);
++      return 0;
++}
++#endif
++EXPORT_SYMBOL_GPL(broadcast_uevent);
++
++#if defined(CONFIG_NET)
+ static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb,
+                               struct netlink_ext_ack *extack)
+ {
diff --git a/target/linux/generic/hack-6.6/920-device_tree_cmdline.patch b/target/linux/generic/hack-6.6/920-device_tree_cmdline.patch
new file mode 100644 (file)
index 0000000..1d78a9d
--- /dev/null
@@ -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
+@@ -1186,6 +1186,9 @@ int __init early_init_dt_scan_chosen(cha
+       p = of_get_flat_dt_prop(node, "bootargs", &l);
+       if (p != NULL && l > 0)
+               strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE));
++      p = of_get_flat_dt_prop(node, "bootargs-append", &l);
++      if (p != NULL && l > 0)
++              strlcat(cmdline, p, min_t(int, strlen(cmdline) + (int)l, COMMAND_LINE_SIZE));
+ handle_cmdline:
+       /*
diff --git a/target/linux/generic/hack-6.6/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch b/target/linux/generic/hack-6.6/930-Revert-Revert-Revert-driver-core-Set-fw_devlink-on-b.patch
new file mode 100644 (file)
index 0000000..1c5fb11
--- /dev/null
@@ -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
+@@ -1657,7 +1657,7 @@ static void device_links_purge(struct de
+ #define FW_DEVLINK_FLAGS_RPM          (FW_DEVLINK_FLAGS_ON | \
+                                        DL_FLAG_PM_RUNTIME)
+-static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON;
++static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
+ static int __init fw_devlink_setup(char *arg)
+ {
+       if (!arg)
diff --git a/target/linux/generic/pending-6.6/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch b/target/linux/generic/pending-6.6/100-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch
new file mode 100644 (file)
index 0000000..0844fcd
--- /dev/null
@@ -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
+@@ -202,6 +202,8 @@ void ftrace_likely_update(struct ftrace_
+       __v;                                                            \
+ })
++#include <asm/rwonce.h>
++
+ #endif /* __KERNEL__ */
+ /*
+@@ -243,6 +245,4 @@ static inline void *offset_to_ptr(const
+  */
+ #define prevent_tail_call_optimization()      mb()
+-#include <asm/rwonce.h>
+-
+ #endif /* __LINUX_COMPILER_H */
diff --git a/target/linux/generic/pending-6.6/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch b/target/linux/generic/pending-6.6/102-MIPS-only-process-negative-stack-offsets-on-stack-tr.patch
new file mode 100644 (file)
index 0000000..d79d03d
--- /dev/null
@@ -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
+@@ -395,6 +395,8 @@ static inline int is_sp_move_ins(union m
+       if (ip->i_format.opcode == addiu_op ||
+           ip->i_format.opcode == daddiu_op) {
++              if (ip->i_format.simmediate > 0)
++                      return 0;
+               *frame_size = -ip->i_format.simmediate;
+               return 1;
+       }
diff --git a/target/linux/generic/pending-6.6/103-kbuild-export-SUBARCH.patch b/target/linux/generic/pending-6.6/103-kbuild-export-SUBARCH.patch
new file mode 100644 (file)
index 0000000..ea7a25d
--- /dev/null
@@ -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
+@@ -599,7 +599,7 @@ endif
+ # Allows the usage of unstable features in stable compilers.
+ export RUSTC_BOOTSTRAP := 1
+-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG
++export ARCH SRCARCH SUBARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG
+ export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN CARGO
+ export HOSTRUSTC KBUILD_HOSTRUSTFLAGS
+ export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL
diff --git a/target/linux/generic/pending-6.6/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch b/target/linux/generic/pending-6.6/111-watchdog-max63xx_wdt-Add-support-for-specifying-WDI-.patch
new file mode 100644 (file)
index 0000000..9dd90ee
--- /dev/null
@@ -0,0 +1,75 @@
+From bd1b9f66d5134e518419f4c4dacf1884c1616983 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 28 Apr 2022 11:13:23 +0200
+Subject: [PATCH] watchdog: max63xx_wdt: Add support for specifying WDI logic
+ via GPIO
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+On some boards is WDI logic of max6370 chip connected via GPIO.
+So extend max63xx_wdt driver to allow specifying WDI logic via GPIO.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/watchdog/max63xx_wdt.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/drivers/watchdog/max63xx_wdt.c
++++ b/drivers/watchdog/max63xx_wdt.c
+@@ -24,6 +24,7 @@
+ #include <linux/io.h>
+ #include <linux/slab.h>
+ #include <linux/property.h>
++#include <linux/gpio/consumer.h>
+ #define DEFAULT_HEARTBEAT 60
+ #define MAX_HEARTBEAT     60
+@@ -50,6 +51,9 @@ struct max63xx_wdt {
+       void __iomem *base;
+       spinlock_t lock;
++      /* GPIOs */
++      struct gpio_desc *gpio_wdi;
++
+       /* WDI and WSET bits write access routines */
+       void (*ping)(struct max63xx_wdt *wdt);
+       void (*set)(struct max63xx_wdt *wdt, u8 set);
+@@ -155,6 +159,17 @@ static const struct watchdog_info max63x
+       .identity = "max63xx Watchdog",
+ };
++static void max63xx_gpio_ping(struct max63xx_wdt *wdt)
++{
++      spin_lock(&wdt->lock);
++
++      gpiod_set_value(wdt->gpio_wdi, 1);
++      udelay(1);
++      gpiod_set_value(wdt->gpio_wdi, 0);
++
++      spin_unlock(&wdt->lock);
++}
++
+ static void max63xx_mmap_ping(struct max63xx_wdt *wdt)
+ {
+       u8 val;
+@@ -222,10 +237,19 @@ static int max63xx_wdt_probe(struct plat
+               return -EINVAL;
+       }
++      wdt->gpio_wdi = devm_gpiod_get(dev, NULL, GPIOD_FLAGS_BIT_DIR_OUT);
++      if (IS_ERR(wdt->gpio_wdi) && PTR_ERR(wdt->gpio_wdi) != -ENOENT)
++              return dev_err_probe(dev, PTR_ERR(wdt->gpio_wdi),
++                                   "unable to request gpio: %ld\n",
++                                   PTR_ERR(wdt->gpio_wdi));
++
+       err = max63xx_mmap_init(pdev, wdt);
+       if (err)
+               return err;
++      if (!IS_ERR(wdt->gpio_wdi))
++              wdt->ping = max63xx_gpio_ping;
++
+       platform_set_drvdata(pdev, &wdt->wdd);
+       watchdog_set_drvdata(&wdt->wdd, wdt);
diff --git a/target/linux/generic/pending-6.6/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch b/target/linux/generic/pending-6.6/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch
new file mode 100644 (file)
index 0000000..099b07e
--- /dev/null
@@ -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/mm_init.c
++++ b/mm/mm_init.c
+@@ -1673,7 +1673,7 @@ static void __init alloc_node_mem_map(st
+       if (pgdat == NODE_DATA(0)) {
+               mem_map = NODE_DATA(0)->node_mem_map;
+               if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
+-                      mem_map -= offset;
++                      mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
+       }
+ #endif
+ }
diff --git a/target/linux/generic/pending-6.6/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch b/target/linux/generic/pending-6.6/140-jffs2-use-.rename2-and-add-RENAME_WHITEOUT-support.patch
new file mode 100644 (file)
index 0000000..b82f3d8
--- /dev/null
@@ -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
+@@ -617,8 +617,8 @@ static int jffs2_rmdir (struct inode *di
+       return ret;
+ }
+-static int jffs2_mknod (struct mnt_idmap *idmap, struct inode *dir_i,
+-                      struct dentry *dentry, umode_t mode, dev_t rdev)
++static int __jffs2_mknod (struct mnt_idmap *idmap, 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;
+@@ -758,7 +758,11 @@ static int jffs2_mknod (struct mnt_idmap
+       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:
+@@ -766,6 +770,19 @@ static int jffs2_mknod (struct mnt_idmap
+       return ret;
+ }
++static int jffs2_mknod (struct mnt_idmap *idmap, struct inode *dir_i,
++                      struct dentry *dentry, umode_t mode, dev_t rdev)
++{
++      return __jffs2_mknod(idmap, dir_i, dentry, mode, rdev, false);
++}
++
++static int jffs2_whiteout (struct mnt_idmap *idmap, struct inode *old_dir,
++                         struct dentry *old_dentry)
++{
++      return __jffs2_mknod(idmap, old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE,
++                           WHITEOUT_DEV, true);
++}
++
+ static int jffs2_rename (struct mnt_idmap *idmap,
+                        struct inode *old_dir_i, struct dentry *old_dentry,
+                        struct inode *new_dir_i, struct dentry *new_dentry,
+@@ -777,7 +794,7 @@ static int jffs2_rename (struct mnt_idma
+       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
+@@ -843,9 +860,14 @@ static int jffs2_rename (struct mnt_idma
+       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(idmap, old_dir_i, old_dentry);
++      else
++              /* Unlink the original */
++              ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
++                                    old_dentry->d_name.name,
++                                    old_dentry->d_name.len, NULL, now);
+       /* We don't touch inode->i_nlink */
diff --git a/target/linux/generic/pending-6.6/141-jffs2-add-RENAME_EXCHANGE-support.patch b/target/linux/generic/pending-6.6/141-jffs2-add-RENAME_EXCHANGE-support.patch
new file mode 100644 (file)
index 0000000..c3a528e
--- /dev/null
@@ -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
+@@ -791,18 +791,31 @@ static int jffs2_rename (struct mnt_idma
+       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;
+@@ -837,7 +850,7 @@ static int jffs2_rename (struct mnt_idma
+       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));
+@@ -863,6 +876,12 @@ static int jffs2_rename (struct mnt_idma
+       if (flags & RENAME_WHITEOUT)
+               /* Replace with whiteout */
+               ret = jffs2_whiteout(idmap, 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),
+@@ -895,7 +914,7 @@ static int jffs2_rename (struct mnt_idma
+               return ret;
+       }
+-      if (d_is_dir(old_dentry))
++      if (d_is_dir(old_dentry) && !(flags & RENAME_EXCHANGE))
+               drop_nlink(old_dir_i);
+       old_dir_i->i_mtime = inode_set_ctime_to_ts(old_dir_i, ITIME(now));
diff --git a/target/linux/generic/pending-6.6/142-jffs2-add-splice-ops.patch b/target/linux/generic/pending-6.6/142-jffs2-add-splice-ops.patch
new file mode 100644 (file)
index 0000000..ea57158
--- /dev/null
@@ -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 =  filemap_splice_read,
++      .splice_write = iter_file_splice_write,
+       .unlocked_ioctl=jffs2_ioctl,
+       .mmap =         generic_file_readonly_mmap,
+       .fsync =        jffs2_fsync,
diff --git a/target/linux/generic/pending-6.6/150-bridge_allow_receiption_on_disabled_port.patch b/target/linux/generic/pending-6.6/150-bridge_allow_receiption_on_disabled_port.patch
new file mode 100644 (file)
index 0000000..d8fd9cd
--- /dev/null
@@ -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
+@@ -244,6 +244,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 */
+@@ -415,6 +418,17 @@ forward:
+               goto defer_stp_filtering;
+       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:
+ defer_stp_filtering:
diff --git a/target/linux/generic/pending-6.6/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch b/target/linux/generic/pending-6.6/151-net-bridge-do-not-send-arp-replies-if-src-and-target.patch
new file mode 100644 (file)
index 0000000..3abeaca
--- /dev/null
@@ -0,0 +1,37 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 4 Jan 2024 15:21:21 +0100
+Subject: [PATCH] net: bridge: do not send arp replies if src and target hw
+ addr is the same
+
+There are broken devices in the wild that handle duplicate IP address
+detection by sending out ARP requests for the IP that they received from a
+DHCP server and refuse the address if they get a reply.
+When proxyarp is enabled, they would go into a loop of requesting an address
+and then NAKing it again.
+
+Link: https://github.com/openwrt/openwrt/issues/14309
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/bridge/br_arp_nd_proxy.c
++++ b/net/bridge/br_arp_nd_proxy.c
+@@ -204,7 +204,10 @@ void br_do_proxy_suppress_arp(struct sk_
+                       if ((p && (p->flags & BR_PROXYARP)) ||
+                           (f->dst && (f->dst->flags & BR_PROXYARP_WIFI)) ||
+                           br_is_neigh_suppress_enabled(f->dst, vid)) {
+-                              if (!vid)
++                              replied = true;
++                              if (!memcmp(n->ha, sha, dev->addr_len))
++                                      replied = false;
++                              else if (!vid)
+                                       br_arp_send(br, p, skb->dev, sip, tip,
+                                                   sha, n->ha, sha, 0, 0);
+                               else
+@@ -212,7 +215,6 @@ void br_do_proxy_suppress_arp(struct sk_
+                                                   sha, n->ha, sha,
+                                                   skb->vlan_proto,
+                                                   skb_vlan_tag_get(skb));
+-                              replied = true;
+                       }
+                       /* If we have replied or as long as we know the
diff --git a/target/linux/generic/pending-6.6/190-rtc-rs5c372-support_alarms_up_to_1_week.patch b/target/linux/generic/pending-6.6/190-rtc-rs5c372-support_alarms_up_to_1_week.patch
new file mode 100644 (file)
index 0000000..2f5c222
--- /dev/null
@@ -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
+@@ -399,7 +399,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)
+@@ -409,6 +411,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);
+@@ -423,12 +449,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 */
+@@ -449,7 +483,9 @@ static int rs5c_set_alarm(struct device
+       /* set alarm */
+       buf[0] = bin2bcd(t->time.tm_min);
+       buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour);
+-      buf[2] = 0x7f;  /* any/all days */
++      /* each bit is the day of the week, 0x7f means all days */
++      buf[2] = (t->time.tm_wday >= 0 && t->time.tm_wday < 7) ?
++                BIT(t->time.tm_wday) : 0x7f;
+       for (i = 0; i < sizeof(buf); i++) {
+               addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);
diff --git a/target/linux/generic/pending-6.6/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch b/target/linux/generic/pending-6.6/191-rtc-rs5c372-let_the_alarm_to_be_used_as_wakeup_source.patch
new file mode 100644 (file)
index 0000000..a29c548
--- /dev/null
@@ -0,0 +1,71 @@
+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
+@@ -832,6 +832,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__);
+@@ -868,6 +869,12 @@ static int rs5c372_probe(struct i2c_clie
+               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;
+@@ -901,6 +908,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.
+        *
+@@ -927,6 +936,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);
+@@ -940,6 +953,10 @@ static int rs5c372_probe(struct i2c_clie
+       if (err)
+               goto exit;
++      /* the rs5c372 alarm only supports a minute accuracy */
++      set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rs5c372->rtc->features);
++      clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rs5c372->rtc->features);
++
+       return 0;
+ exit:
diff --git a/target/linux/generic/pending-6.6/203-kallsyms_uncompressed.patch b/target/linux/generic/pending-6.6/203-kallsyms_uncompressed.patch
new file mode 100644 (file)
index 0000000..94b2260
--- /dev/null
@@ -0,0 +1,167 @@
+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
+@@ -1460,6 +1460,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
+@@ -69,6 +69,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++;
+@@ -101,6 +106,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
+@@ -62,6 +62,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;
+ static int lto_clang;
+@@ -453,13 +454,15 @@ static void write_src(void)
+       }
+       printf("\n");
+-      /*
+-       * Now that we wrote out the compressed symbol names, restore the
+-       * original names, which are needed in some of the later steps.
+-       */
+-      for (i = 0; i < table_cnt; i++) {
+-              expand_symbol(table[i]->sym, table[i]->len, buf);
+-              strcpy((char *)table[i]->sym, buf);
++      if (!uncompressed) {
++              /*
++               * Now that we wrote out the compressed symbol names, restore the
++               * original names, which are needed in some of the later steps.
++               */
++              for (i = 0; i < table_cnt; i++) {
++                      expand_symbol(table[i]->sym, table[i]->len, buf);
++                      strcpy((char *)table[i]->sym, buf);
++              }
+       }
+       output_label("kallsyms_markers");
+@@ -469,20 +472,22 @@ static void write_src(void)
+       free(markers);
+-      output_label("kallsyms_token_table");
+-      off = 0;
+-      for (i = 0; i < 256; i++) {
+-              best_idx[i] = off;
+-              expand_symbol(best_table[i], best_table_len[i], buf);
+-              printf("\t.asciz\t\"%s\"\n", buf);
+-              off += strlen(buf) + 1;
+-      }
+-      printf("\n");
++      if (!uncompressed) {
++              output_label("kallsyms_token_table");
++              off = 0;
++              for (i = 0; i < 256; i++) {
++                      best_idx[i] = off;
++                      expand_symbol(best_table[i], best_table_len[i], buf);
++                      printf("\t.asciz\t\"%s\"\n", buf);
++                      off += strlen(buf) + 1;
++              }
++              printf("\n");
+-      output_label("kallsyms_token_index");
+-      for (i = 0; i < 256; i++)
+-              printf("\t.short\t%d\n", best_idx[i]);
+-      printf("\n");
++              output_label("kallsyms_token_index");
++              for (i = 0; i < 256; i++)
++                      printf("\t.short\t%d\n", best_idx[i]);
++              printf("\n");
++      }
+       if (!base_relative)
+               output_label("kallsyms_addresses");
+@@ -582,6 +587,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];
+@@ -654,6 +662,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--) {
+@@ -815,6 +826,7 @@ int main(int argc, char **argv)
+                       {"absolute-percpu", no_argument, &absolute_percpu, 1},
+                       {"base-relative",   no_argument, &base_relative,   1},
+                       {"lto-clang",       no_argument, &lto_clang,       1},
++                      {"uncompressed",   no_argument, &uncompressed,   1},
+                       {},
+               };
+--- a/scripts/link-vmlinux.sh
++++ b/scripts/link-vmlinux.sh
+@@ -165,6 +165,10 @@ kallsyms()
+               kallsymopt="${kallsymopt} --lto-clang"
+       fi
++      if is_enabled CONFIG_KALLSYMS_UNCOMPRESSED; then
++              kallsymopt="${kallsymopt} --uncompressed"
++      fi
++
+       info KSYMS ${2}
+       scripts/kallsyms ${kallsymopt} ${1} > ${2}
+ }
diff --git a/target/linux/generic/pending-6.6/205-backtrace_module_info.patch b/target/linux/generic/pending-6.6/205-backtrace_module_info.patch
new file mode 100644 (file)
index 0000000..34018e2
--- /dev/null
@@ -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
+@@ -982,8 +982,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')
+@@ -1004,8 +1006,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->mem[MOD_TEXT].base,
++                       mod->mem[MOD_TEXT].size);
+ #endif
++      return string(buf, end, sym, spec);
+ }
+ static const struct printf_spec default_str_spec = {
diff --git a/target/linux/generic/pending-6.6/240-remove-unsane-filenames-from-deps_initramfs-list.patch b/target/linux/generic/pending-6.6/240-remove-unsane-filenames-from-deps_initramfs-list.patch
new file mode 100644 (file)
index 0000000..9e78284
--- /dev/null
@@ -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
+@@ -56,6 +56,8 @@ hostprogs := gen_init_cpio
+ # The dependency list is generated by gen_initramfs.sh -l
+ -include $(obj)/.initramfs_data.cpio.d
++deps_initramfs := $(foreach v,$(deps_initramfs),$(if $(findstring :,$(v)),,$(v)))
++
+ # do not try to update files included in initramfs
+ $(deps_initramfs): ;
diff --git a/target/linux/generic/pending-6.6/270-platform-mikrotik-build-bits.patch b/target/linux/generic/pending-6.6/270-platform-mikrotik-build-bits.patch
new file mode 100644 (file)
index 0000000..7ca84e0
--- /dev/null
@@ -0,0 +1,31 @@
+From c2deb5ef01a0ef09088832744cbace9e239a6ee0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org>
+Date: Sat, 28 Mar 2020 12:11:50 +0100
+Subject: [PATCH] generic: platform/mikrotik build bits (5.4)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds platform/mikrotik kernel build bits
+
+Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
+---
+ drivers/platform/Kconfig  | 2 ++
+ drivers/platform/Makefile | 1 +
+ 2 files changed, 3 insertions(+)
+
+--- a/drivers/platform/Kconfig
++++ b/drivers/platform/Kconfig
+@@ -14,3 +14,5 @@ source "drivers/platform/olpc/Kconfig"
+ source "drivers/platform/surface/Kconfig"
+ source "drivers/platform/x86/Kconfig"
++
++source "drivers/platform/mikrotik/Kconfig"
+--- a/drivers/platform/Makefile
++++ b/drivers/platform/Makefile
+@@ -11,3 +11,4 @@ obj-$(CONFIG_OLPC_EC)                += olpc/
+ obj-$(CONFIG_GOLDFISH)                += goldfish/
+ obj-$(CONFIG_CHROME_PLATFORMS)        += chrome/
+ obj-$(CONFIG_SURFACE_PLATFORMS)       += surface/
++obj-$(CONFIG_MIKROTIK)                += mikrotik/
diff --git a/target/linux/generic/pending-6.6/300-mips_expose_boot_raw.patch b/target/linux/generic/pending-6.6/300-mips_expose_boot_raw.patch
new file mode 100644 (file)
index 0000000..ebeeb7b
--- /dev/null
@@ -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
+@@ -1013,9 +1013,6 @@ config FW_ARC
+ config ARCH_MAY_HAVE_PC_FDC
+       bool
+-config BOOT_RAW
+-      bool
+-
+ config CEVT_BCM1480
+       bool
+@@ -2996,6 +2993,18 @@ choice
+               bool "Extend builtin kernel arguments with bootloader arguments"
+ endchoice
++config BOOT_RAW
++      bool "Enable the kernel to be executed from the load address"
++      default n
++      help
++       Allow the kernel to be executed from the load address for
++       bootloaders which cannot read the ELF format. This places
++       a jump to start_kernel at the load address.
++
++       If unsure, say N.
++
++
++
+ endmenu
+ config LOCKDEP_SUPPORT
diff --git a/target/linux/generic/pending-6.6/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch b/target/linux/generic/pending-6.6/301-MIPS-Add-barriers-between-dcache-icache-flushes.patch
new file mode 100644 (file)
index 0000000..b3cb5f0
--- /dev/null
@@ -0,0 +1,71 @@
+From e6e6ef4275978823ec3a84133fc91f4ffbef5c84 Mon Sep 17 00:00:00 2001
+From: Paul Burton <paul.burton@imgtec.com>
+Date: Mon, 22 Feb 2016 18:09:44 +0000
+Subject: [PATCH] MIPS: Add barriers between dcache & icache flushes
+
+Index-based cache operations may be arbitrarily reordered by out of
+order CPUs. Thus code which writes back the dcache & then invalidates
+the icache using indexed cache ops must include a barrier between
+operating on the 2 caches in order to prevent the scenario in which:
+
+  - icache invalidation occurs.
+
+  - icache fetch occurs, due to speculation.
+
+  - dcache writeback occurs.
+
+If the above were allowed to happen then the icache would contain stale
+data. Forcing the dcache writeback to complete before the icache
+invalidation avoids this.
+
+Signed-off-by: Paul Burton <paul.burton@imgtec.com>
+Cc: James Hogan <james.hogan@imgtec.com>
+---
+ arch/mips/mm/c-r4k.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+--- a/arch/mips/mm/c-r4k.c
++++ b/arch/mips/mm/c-r4k.c
+@@ -403,6 +403,7 @@ static inline void local_r4k___flush_cac
+       default:
+               r4k_blast_dcache();
++              mb(); /* cache instructions may be reordered */
+               r4k_blast_icache();
+               break;
+       }
+@@ -483,8 +484,10 @@ static inline void local_r4k_flush_cache
+       if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc))
+               r4k_blast_dcache();
+       /* If executable, blast stale lines from icache */
+-      if (exec)
++      if (exec) {
++              mb(); /* cache instructions may be reordered */
+               r4k_blast_icache();
++      }
+ }
+ static void r4k_flush_cache_range(struct vm_area_struct *vma,
+@@ -586,8 +589,13 @@ static inline void local_r4k_flush_cache
+       if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) {
+               vaddr ? r4k_blast_dcache_page(addr) :
+                       r4k_blast_dcache_user_page(addr);
+-              if (exec && !cpu_icache_snoops_remote_store)
++              if (exec)
++                      mb(); /* cache instructions may be reordered */
++
++              if (exec && !cpu_icache_snoops_remote_store) {
+                       r4k_blast_scache_page(addr);
++                      mb(); /* cache instructions may be reordered */
++              }
+       }
+       if (exec) {
+               if (vaddr && cpu_has_vtag_icache && mm == current->active_mm) {
+@@ -654,6 +662,7 @@ static inline void __local_r4k_flush_ica
+                       else
+                               blast_dcache_range(start, end);
+               }
++              mb(); /* cache instructions may be reordered */
+       }
+       if (type == R4K_INDEX ||
diff --git a/target/linux/generic/pending-6.6/302-mips_no_branch_likely.patch b/target/linux/generic/pending-6.6/302-mips_no_branch_likely.patch
new file mode 100644 (file)
index 0000000..669aa81
--- /dev/null
@@ -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
+@@ -94,7 +94,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 -Wa,-msoft-float
+ LDFLAGS_vmlinux                       += -G 0 -static -n -nostdlib
+ KBUILD_AFLAGS_MODULE          += -mlong-calls
diff --git a/target/linux/generic/pending-6.6/305-mips_module_reloc.patch b/target/linux/generic/pending-6.6/305-mips_module_reloc.patch
new file mode 100644 (file)
index 0000000..6d13574
--- /dev/null
@@ -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
+@@ -97,8 +97,18 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin
+ cflags-y                      += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely
+ cflags-y                      += -msoft-float -Wa,-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
+@@ -32,23 +32,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);
+@@ -56,13 +294,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;
+ }
+@@ -442,9 +684,36 @@ int module_finalize(const Elf_Ehdr *hdr,
+               list_add(&me->arch.dbe_list, &dbe_list);
+               spin_unlock_irq(&dbe_lock);
+       }
++
++      /* Get rid of the fixup trampoline if we're running the module
++       * from physically mapped address space */
++      if (me->arch.phys_plt_offset == 0) {
++              __module_free(me->arch.phys_plt_tbl);
++              me->arch.phys_plt_tbl = NULL;
++      }
++      if (me->arch.virt_plt_offset == 0) {
++              __module_free(me->arch.virt_plt_tbl);
++              me->arch.virt_plt_tbl = NULL;
++      }
++
+       return 0;
+ }
++void module_arch_freeing_init(struct module *mod)
++{
++      if (mod->state == MODULE_STATE_LIVE)
++              return;
++
++      if (mod->arch.phys_plt_tbl) {
++              __module_free(mod->arch.phys_plt_tbl);
++              mod->arch.phys_plt_tbl = NULL;
++      }
++      if (mod->arch.virt_plt_tbl) {
++              __module_free(mod->arch.virt_plt_tbl);
++              mod->arch.virt_plt_tbl = NULL;
++      }
++}
++
+ void module_arch_cleanup(struct module *mod)
+ {
+       spin_lock_irq(&dbe_lock);
diff --git a/target/linux/generic/pending-6.6/308-mips32r2_tune.patch b/target/linux/generic/pending-6.6/308-mips32r2_tune.patch
new file mode 100644 (file)
index 0000000..b120580
--- /dev/null
@@ -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
+@@ -163,7 +163,7 @@ cflags-$(CONFIG_CPU_R4300) += -march=r43
+ cflags-$(CONFIG_CPU_R4X00)    += -march=r4600 -Wa,--trap
+ cflags-$(CONFIG_CPU_TX49XX)   += -march=r4600 -Wa,--trap
+ cflags-$(CONFIG_CPU_MIPS32_R1)        += -march=mips32 -Wa,--trap
+-cflags-$(CONFIG_CPU_MIPS32_R2)        += -march=mips32r2 -Wa,--trap
++cflags-$(CONFIG_CPU_MIPS32_R2)        += -march=mips32r2 -mtune=34kc -Wa,--trap
+ cflags-$(CONFIG_CPU_MIPS32_R5)        += -march=mips32r5 -Wa,--trap -modd-spreg
+ cflags-$(CONFIG_CPU_MIPS32_R6)        += -march=mips32r6 -Wa,--trap -modd-spreg
+ cflags-$(CONFIG_CPU_MIPS64_R1)        += -march=mips64 -Wa,--trap
diff --git a/target/linux/generic/pending-6.6/310-arm_module_unresolved_weak_sym.patch b/target/linux/generic/pending-6.6/310-arm_module_unresolved_weak_sym.patch
new file mode 100644 (file)
index 0000000..54cc9ba
--- /dev/null
@@ -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
+@@ -146,6 +146,10 @@ apply_relocate(Elf32_Shdr *sechdrs, cons
+                       return -ENOEXEC;
+               }
++              if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) &&
++                  ELF_ST_BIND(sym->st_info) == STB_WEAK)
++                      continue;
++
+               loc = dstsec->sh_addr + rel->r_offset;
+               switch (ELF32_R_TYPE(rel->r_info)) {
diff --git a/target/linux/generic/pending-6.6/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch b/target/linux/generic/pending-6.6/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
new file mode 100644 (file)
index 0000000..3f553b2
--- /dev/null
@@ -0,0 +1,282 @@
+From: Yousong Zhou <yszhou4tech@gmail.com>
+Subject: MIPS: kexec: Accept command line parameters from userspace.
+
+Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
+---
+ arch/mips/kernel/machine_kexec.c   |  153 +++++++++++++++++++++++++++++++-----
+ arch/mips/kernel/machine_kexec.h   |   20 +++++
+ arch/mips/kernel/relocate_kernel.S |   21 +++--
+ 3 files changed, 167 insertions(+), 27 deletions(-)
+ create mode 100644 arch/mips/kernel/machine_kexec.h
+
+--- a/arch/mips/kernel/machine_kexec.c
++++ b/arch/mips/kernel/machine_kexec.c
+@@ -9,14 +9,11 @@
+ #include <linux/delay.h>
+ #include <linux/libfdt.h>
++#include <asm/bootinfo.h>
+ #include <asm/cacheflush.h>
+ #include <asm/page.h>
+-
+-extern const unsigned char relocate_new_kernel[];
+-extern const size_t relocate_new_kernel_size;
+-
+-extern unsigned long kexec_start_address;
+-extern unsigned long kexec_indirection_page;
++#include <linux/uaccess.h>
++#include "machine_kexec.h"
+ static unsigned long reboot_code_buffer;
+@@ -30,6 +27,101 @@ void (*_crash_smp_send_stop)(void) = NUL
+ void (*_machine_kexec_shutdown)(void) = NULL;
+ void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
++static void machine_kexec_print_args(void)
++{
++      unsigned long argc = (int)kexec_args[0];
++      int i;
++
++      pr_info("kexec_args[0] (argc): %lu\n", argc);
++      pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
++      pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
++      pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
++
++      for (i = 0; i < argc; i++) {
++              pr_info("kexec_argv[%d] = %p, %s\n",
++                              i, kexec_argv[i], kexec_argv[i]);
++      }
++}
++
++static void machine_kexec_init_argv(struct kimage *image)
++{
++      void __user *buf = NULL;
++      size_t bufsz;
++      size_t size;
++      int i;
++
++      bufsz = 0;
++      for (i = 0; i < image->nr_segments; i++) {
++              struct kexec_segment *seg;
++
++              seg = &image->segment[i];
++              if (seg->bufsz < 6)
++                      continue;
++
++              if (strncmp((char *) seg->buf, "kexec ", 6))
++                      continue;
++
++              buf = seg->buf;
++              bufsz = seg->bufsz;
++              break;
++      }
++
++      if (!buf)
++              return;
++
++      size = KEXEC_COMMAND_LINE_SIZE;
++      size = min(size, bufsz);
++      if (size < bufsz)
++              pr_warn("kexec command line truncated to %zd bytes\n", size);
++
++      /* Copy to kernel space */
++      if (copy_from_user(kexec_argv_buf, buf, size))
++              pr_warn("kexec command line copy to kernel space failed\n");
++
++      kexec_argv_buf[size - 1] = 0;
++}
++
++static void machine_kexec_parse_argv(struct kimage *image)
++{
++      char *reboot_code_buffer;
++      int reloc_delta;
++      char *ptr;
++      int argc;
++      int i;
++
++      ptr = kexec_argv_buf;
++      argc = 0;
++
++      /*
++       * convert command line string to array of parameters
++       * (as bootloader does).
++       */
++      while (ptr && *ptr && (KEXEC_MAX_ARGC > argc)) {
++              if (*ptr == ' ') {
++                      *ptr++ = '\0';
++                      continue;
++              }
++
++              kexec_argv[argc++] = ptr;
++              ptr = strchr(ptr, ' ');
++      }
++
++      if (!argc)
++              return;
++
++      kexec_args[0] = argc;
++      kexec_args[1] = (unsigned long)kexec_argv;
++      kexec_args[2] = 0;
++      kexec_args[3] = 0;
++
++      reboot_code_buffer = page_address(image->control_code_page);
++      reloc_delta = reboot_code_buffer - (char *)kexec_relocate_new_kernel;
++
++      kexec_args[1] += reloc_delta;
++      for (i = 0; i < argc; i++)
++              kexec_argv[i] += reloc_delta;
++}
++
+ static void kexec_image_info(const struct kimage *kimage)
+ {
+       unsigned long i;
+@@ -99,6 +191,18 @@ machine_kexec_prepare(struct kimage *kim
+ #endif
+       kexec_image_info(kimage);
++      /*
++       * Whenever arguments passed from kexec-tools, Init the arguments as
++       * the original ones to try avoiding booting failure.
++       */
++
++      kexec_args[0] = fw_arg0;
++      kexec_args[1] = fw_arg1;
++      kexec_args[2] = fw_arg2;
++      kexec_args[3] = fw_arg3;
++
++      machine_kexec_init_argv(kimage);
++      machine_kexec_parse_argv(kimage);
+       if (_machine_kexec_prepare)
+               return _machine_kexec_prepare(kimage);
+@@ -161,7 +265,7 @@ machine_crash_shutdown(struct pt_regs *r
+ void kexec_nonboot_cpu_jump(void)
+ {
+       local_flush_icache_range((unsigned long)relocated_kexec_smp_wait,
+-                               reboot_code_buffer + relocate_new_kernel_size);
++                               reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE);
+       relocated_kexec_smp_wait(NULL);
+ }
+@@ -199,7 +303,7 @@ void kexec_reboot(void)
+        * machine_kexec() CPU.
+        */
+       local_flush_icache_range(reboot_code_buffer,
+-                               reboot_code_buffer + relocate_new_kernel_size);
++                               reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE);
+       do_kexec = (void *)reboot_code_buffer;
+       do_kexec();
+@@ -212,10 +316,12 @@ machine_kexec(struct kimage *image)
+       unsigned long *ptr;
+       reboot_code_buffer =
+-        (unsigned long)page_address(image->control_code_page);
++              (unsigned long)page_address(image->control_code_page);
++      pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer);
+       kexec_start_address =
+               (unsigned long) phys_to_virt(image->start);
++      pr_info("kexec_start_address = %p\n", (void *)kexec_start_address);
+       if (image->type == KEXEC_TYPE_DEFAULT) {
+               kexec_indirection_page =
+@@ -223,9 +329,19 @@ machine_kexec(struct kimage *image)
+       } else {
+               kexec_indirection_page = (unsigned long)&image->head;
+       }
++      pr_info("kexec_indirection_page = %p\n", (void *)kexec_indirection_page);
+-      memcpy((void*)reboot_code_buffer, relocate_new_kernel,
+-             relocate_new_kernel_size);
++      pr_info("Where is memcpy: %p\n", memcpy);
++      pr_info("kexec_relocate_new_kernel = %p, kexec_relocate_new_kernel_end = %p\n",
++              (void *)kexec_relocate_new_kernel, &kexec_relocate_new_kernel_end);
++      pr_info("Copy %lu bytes from %p to %p\n", KEXEC_RELOCATE_NEW_KERNEL_SIZE,
++              (void *)kexec_relocate_new_kernel, (void *)reboot_code_buffer);
++      memcpy((void*)reboot_code_buffer, kexec_relocate_new_kernel,
++             KEXEC_RELOCATE_NEW_KERNEL_SIZE);
++
++      pr_info("Before _print_args().\n");
++      machine_kexec_print_args();
++      pr_info("Before eval loop.\n");
+       /*
+        * The generic kexec code builds a page list with physical
+@@ -256,7 +372,7 @@ machine_kexec(struct kimage *image)
+ #ifdef CONFIG_SMP
+       /* All secondary cpus now may jump to kexec_wait cycle */
+       relocated_kexec_smp_wait = reboot_code_buffer +
+-              (void *)(kexec_smp_wait - relocate_new_kernel);
++              (void *)(kexec_smp_wait - kexec_relocate_new_kernel);
+       smp_wmb();
+       atomic_set(&kexec_ready_to_reboot, 1);
+ #endif
+--- /dev/null
++++ b/arch/mips/kernel/machine_kexec.h
+@@ -0,0 +1,20 @@
++#ifndef _MACHINE_KEXEC_H
++#define _MACHINE_KEXEC_H
++
++#ifndef __ASSEMBLY__
++extern const unsigned char kexec_relocate_new_kernel[];
++extern unsigned long kexec_relocate_new_kernel_end;
++extern unsigned long kexec_start_address;
++extern unsigned long kexec_indirection_page;
++
++extern char kexec_argv_buf[];
++extern char *kexec_argv[];
++
++#define KEXEC_RELOCATE_NEW_KERNEL_SIZE        ((unsigned long)&kexec_relocate_new_kernel_end - (unsigned long)kexec_relocate_new_kernel)
++#endif /* !__ASSEMBLY__ */
++
++#define KEXEC_COMMAND_LINE_SIZE               256
++#define KEXEC_ARGV_SIZE                       (KEXEC_COMMAND_LINE_SIZE / 16)
++#define KEXEC_MAX_ARGC                        (KEXEC_ARGV_SIZE / sizeof(long))
++
++#endif
+--- a/arch/mips/kernel/relocate_kernel.S
++++ b/arch/mips/kernel/relocate_kernel.S
+@@ -10,10 +10,11 @@
+ #include <asm/mipsregs.h>
+ #include <asm/stackframe.h>
+ #include <asm/addrspace.h>
++#include "machine_kexec.h"
+ #include <kernel-entry-init.h>
+-LEAF(relocate_new_kernel)
++LEAF(kexec_relocate_new_kernel)
+       PTR_L a0,       arg0
+       PTR_L a1,       arg1
+       PTR_L a2,       arg2
+@@ -98,7 +99,7 @@ done:
+ #endif
+       /* jump to kexec_start_address */
+       j               s1
+-      END(relocate_new_kernel)
++      END(kexec_relocate_new_kernel)
+ #ifdef CONFIG_SMP
+ /*
+@@ -177,8 +178,15 @@ EXPORT(kexec_indirection_page)
+       PTR_WD          0
+       .size           kexec_indirection_page, PTRSIZE
+-relocate_new_kernel_end:
++kexec_argv_buf:
++      EXPORT(kexec_argv_buf)
++      .skip           KEXEC_COMMAND_LINE_SIZE
++      .size           kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE
++
++kexec_argv:
++      EXPORT(kexec_argv)
++      .skip           KEXEC_ARGV_SIZE
++      .size           kexec_argv, KEXEC_ARGV_SIZE
+-EXPORT(relocate_new_kernel_size)
+-      PTR_WD          relocate_new_kernel_end - relocate_new_kernel
+-      .size           relocate_new_kernel_size, PTRSIZE
++kexec_relocate_new_kernel_end:
++      EXPORT(kexec_relocate_new_kernel_end)
diff --git a/target/linux/generic/pending-6.6/332-arc-add-OWRTDTB-section.patch b/target/linux/generic/pending-6.6/332-arc-add-OWRTDTB-section.patch
new file mode 100644 (file)
index 0000000..5b943f3
--- /dev/null
@@ -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
+@@ -450,6 +450,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;
+@@ -488,7 +490,7 @@ void __init handle_uboot_args(void)
+ ignore_uboot_args:
+       if (use_embedded_dtb) {
+-              machine_desc = setup_machine_fdt(__dtb_start);
++              machine_desc = setup_machine_fdt(&__image_dtb);
+               if (!machine_desc)
+                       panic("Embedded DT invalid\n");
+       }
+--- a/arch/arc/kernel/vmlinux.lds.S
++++ b/arch/arc/kernel/vmlinux.lds.S
+@@ -27,6 +27,19 @@ SECTIONS
+       . = CONFIG_LINUX_LINK_BASE;
++      /*
++      * In OpenWRT we want to patch built binary embedding .dtb of choice.
++      * This is implemented with "patch-dtb" utility which searches for
++      * "OWRTDTB:" string in first 16k of image and if it is found
++      * copies .dtb right after mentioned string.
++      *
++      * Note: "OWRTDTB:" won't be overwritten with .dtb, .dtb will follow it.
++      */
++      .owrt : {
++              *(.owrt)
++      . = ALIGN(PAGE_SIZE);
++      }
++
+       _int_vec_base_lds = .;
+       .vector : {
+               *(.vector)
diff --git a/target/linux/generic/pending-6.6/333-arc-enable-unaligned-access-in-kernel-mode.patch b/target/linux/generic/pending-6.6/333-arc-enable-unaligned-access-in-kernel-mode.patch
new file mode 100644 (file)
index 0000000..1848a84
--- /dev/null
@@ -0,0 +1,24 @@
+From: Alexey Brodkin <abrodkin@synopsys.com>
+Subject: arc: enable unaligned access in kernel mode
+
+This enables misaligned access handling even in kernel mode.
+Some wireless drivers (ath9k-htc and mt7601u) use misaligned accesses
+here and there and to cope with that without fixing stuff in the drivers
+we're just gracefully handling it on ARC.
+
+Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
+---
+ arch/arc/kernel/unaligned.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arc/kernel/unaligned.c
++++ b/arch/arc/kernel/unaligned.c
+@@ -202,7 +202,7 @@ int misaligned_fixup(unsigned long addre
+       char buf[TASK_COMM_LEN];
+       /* handle user mode only and only if enabled by sysadmin */
+-      if (!user_mode(regs) || !unaligned_enabled)
++      if (!unaligned_enabled)
+               return 1;
+       if (no_unaligned_warning) {
diff --git a/target/linux/generic/pending-6.6/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch b/target/linux/generic/pending-6.6/342-powerpc-Enable-kernel-XZ-compression-option-on-PPC_8.patch
new file mode 100644 (file)
index 0000000..71173b0
--- /dev/null
@@ -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
+@@ -251,7 +251,7 @@ config PPC
+       select HAVE_KERNEL_GZIP
+       select HAVE_KERNEL_LZMA                 if DEFAULT_UIMAGE
+       select HAVE_KERNEL_LZO                  if DEFAULT_UIMAGE
+-      select HAVE_KERNEL_XZ                   if PPC_BOOK3S || 44x
++      select HAVE_KERNEL_XZ                   if PPC_BOOK3S || 44x || PPC_85xx
+       select HAVE_KPROBES
+       select HAVE_KPROBES_ON_FTRACE
+       select HAVE_KRETPROBES
diff --git a/target/linux/generic/pending-6.6/350-mips-kernel-fix-detect_memory_region-function.patch b/target/linux/generic/pending-6.6/350-mips-kernel-fix-detect_memory_region-function.patch
new file mode 100644 (file)
index 0000000..3bf7ae9
--- /dev/null
@@ -0,0 +1,74 @@
+From: Shiji Yang <yangshiji66@outlook.com>
+Date: Wed, 13 Mar 2024 20:28:37 +0800
+Subject: [PATCH] mips: kernel: fix detect_memory_region() function
+
+1. Do not use memcmp() on unallocated memory, as the new introduced
+   fortify dynamic object size check[1] will report unexpected result.
+2. Use a fixed pattern instead of a random function pointer as the
+   magic value.
+3. Flip magic value and double check it.
+4. Enable this feature only for 32-bit CPUs. Currently, only ath79 and
+   ralink CPUs are using it.
+
+[1] 439a1bcac648 ("fortify: Use __builtin_dynamic_object_size() when available")
+Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
+---
+ arch/mips/include/asm/bootinfo.h |  2 ++
+ arch/mips/kernel/setup.c         | 17 ++++++++++++-----
+ 2 files changed, 14 insertions(+), 5 deletions(-)
+
+--- a/arch/mips/include/asm/bootinfo.h
++++ b/arch/mips/include/asm/bootinfo.h
+@@ -93,7 +93,9 @@ const char *get_system_type(void);
+ extern unsigned long mips_machtype;
++#ifndef CONFIG_64BIT
+ extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min,  phys_addr_t sz_max);
++#endif
+ extern void prom_init(void);
+ extern void prom_free_prom_memory(void);
+--- a/arch/mips/kernel/setup.c
++++ b/arch/mips/kernel/setup.c
+@@ -90,21 +90,27 @@ static struct resource bss_resource = {
+ unsigned long __kaslr_offset __ro_after_init;
+ EXPORT_SYMBOL(__kaslr_offset);
+-static void *detect_magic __initdata = detect_memory_region;
+-
+ #ifdef CONFIG_MIPS_AUTO_PFN_OFFSET
+ unsigned long ARCH_PFN_OFFSET;
+ EXPORT_SYMBOL(ARCH_PFN_OFFSET);
+ #endif
++#ifndef CONFIG_64BIT
++static u32 detect_magic __initdata;
++#define MIPS_MEM_TEST_PATTERN         0xaa5555aa
++
+ void __init detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max)
+ {
+-      void *dm = &detect_magic;
++      void *dm = (void *)KSEG1ADDR(&detect_magic);
+       phys_addr_t size;
+       for (size = sz_min; size < sz_max; size <<= 1) {
+-              if (!memcmp(dm, dm + size, sizeof(detect_magic)))
+-                      break;
++              __raw_writel(MIPS_MEM_TEST_PATTERN, dm);
++              if (__raw_readl(dm) == __raw_readl(dm + size)) {
++                      __raw_writel(~MIPS_MEM_TEST_PATTERN, dm);
++                      if (__raw_readl(dm) == __raw_readl(dm + size))
++                              break;
++              }
+       }
+       pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n",
+@@ -115,6 +121,7 @@ void __init detect_memory_region(phys_ad
+       memblock_add(start, size);
+ }
++#endif /* CONFIG_64BIT */
+ /*
+  * Manage initrd
diff --git a/target/linux/generic/pending-6.6/400-mtd-mtdsplit-support.patch b/target/linux/generic/pending-6.6/400-mtd-mtdsplit-support.patch
new file mode 100644 (file)
index 0000000..bd1c3a1
--- /dev/null
@@ -0,0 +1,328 @@
+From 39717277d5c87bdb183cf2f258957b44ba99b4df Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 11:47:35 +0200
+Subject: [PATCH] mtd: mtdsplit support
+
+---
+ drivers/mtd/Kconfig            |  19 ++++
+ drivers/mtd/Makefile           |   2 +
+ drivers/mtd/mtdpart.c          | 169 ++++++++++++++++++++++++++++-----
+ include/linux/mtd/mtd.h        |  25 +++++
+ include/linux/mtd/partitions.h |   7 ++
+ 5 files changed, 197 insertions(+), 25 deletions(-)
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -12,6 +12,25 @@ menuconfig MTD
+ if MTD
++menu "OpenWrt specific MTD options"
++
++config MTD_ROOTFS_ROOT_DEV
++      bool "Automatically set 'rootfs' partition to be root filesystem"
++      default y
++
++config MTD_SPLIT_FIRMWARE
++      bool "Automatically split firmware partition for kernel+rootfs"
++      default y
++
++config MTD_SPLIT_FIRMWARE_NAME
++      string "Firmware partition name"
++      depends on MTD_SPLIT_FIRMWARE
++      default "firmware"
++
++source "drivers/mtd/mtdsplit/Kconfig"
++
++endmenu
++
+ config MTD_TESTS
+       tristate "MTD tests support (DANGEROUS)"
+       depends on m
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -9,6 +9,8 @@ mtd-y                          := mtdcore.o mtdsuper.o mtdconc
+ obj-y                         += parsers/
++obj-$(CONFIG_MTD_SPLIT)               += mtdsplit/
++
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_BLKDEVS)     += mtd_blkdevs.o
+ obj-$(CONFIG_MTD_BLOCK)               += mtdblock.o
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -15,11 +15,13 @@
+ #include <linux/kmod.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/magic.h>
+ #include <linux/err.h>
+ #include <linux/of.h>
+ #include <linux/of_platform.h>
+ #include "mtdcore.h"
++#include "mtdsplit/mtdsplit.h"
+ /*
+  * MTD methods which simply translate the effective address and pass through
+@@ -242,6 +244,147 @@ static int mtd_add_partition_attrs(struc
+       return ret;
+ }
++static DEFINE_SPINLOCK(part_parser_lock);
++static LIST_HEAD(part_parsers);
++
++static struct mtd_part_parser *mtd_part_parser_get(const char *name)
++{
++      struct mtd_part_parser *p, *ret = NULL;
++
++      spin_lock(&part_parser_lock);
++
++      list_for_each_entry(p, &part_parsers, list)
++              if (!strcmp(p->name, name) && try_module_get(p->owner)) {
++                      ret = p;
++                      break;
++              }
++
++      spin_unlock(&part_parser_lock);
++
++      return ret;
++}
++
++static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
++{
++      module_put(p->owner);
++}
++
++static struct mtd_part_parser *
++get_partition_parser_by_type(enum mtd_parser_type type,
++                           struct mtd_part_parser *start)
++{
++      struct mtd_part_parser *p, *ret = NULL;
++
++      spin_lock(&part_parser_lock);
++
++      p = list_prepare_entry(start, &part_parsers, list);
++      if (start)
++              mtd_part_parser_put(start);
++
++      list_for_each_entry_continue(p, &part_parsers, list) {
++              if (p->type == type && try_module_get(p->owner)) {
++                      ret = p;
++                      break;
++              }
++      }
++
++      spin_unlock(&part_parser_lock);
++
++      return ret;
++}
++
++static int parse_mtd_partitions_by_type(struct mtd_info *master,
++                                      enum mtd_parser_type type,
++                                      const struct mtd_partition **pparts,
++                                      struct mtd_part_parser_data *data)
++{
++      struct mtd_part_parser *prev = NULL;
++      int ret = 0;
++
++      while (1) {
++              struct mtd_part_parser *parser;
++
++              parser = get_partition_parser_by_type(type, prev);
++              if (!parser)
++                      break;
++
++              ret = (*parser->parse_fn)(master, pparts, data);
++
++              if (ret > 0) {
++                      mtd_part_parser_put(parser);
++                      printk(KERN_NOTICE
++                             "%d %s partitions found on MTD device %s\n",
++                             ret, parser->name, master->name);
++                      break;
++              }
++
++              prev = parser;
++      }
++
++      return ret;
++}
++
++static int
++run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
++{
++      struct mtd_partition *parts;
++      int nr_parts;
++      int i;
++
++      nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
++                                              NULL);
++      if (nr_parts <= 0)
++              return nr_parts;
++
++      if (WARN_ON(!parts))
++              return 0;
++
++      for (i = 0; i < nr_parts; i++) {
++              /* adjust partition offsets */
++              parts[i].offset += child->part.offset;
++
++              mtd_add_partition(child->parent,
++                                parts[i].name,
++                                parts[i].offset,
++                                parts[i].size);
++      }
++
++      kfree(parts);
++
++      return nr_parts;
++}
++
++#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
++#define SPLIT_FIRMWARE_NAME   CONFIG_MTD_SPLIT_FIRMWARE_NAME
++#else
++#define SPLIT_FIRMWARE_NAME   "unused"
++#endif
++
++static void split_firmware(struct mtd_info *master, struct mtd_info *part)
++{
++      run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
++}
++
++static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
++{
++      static int rootfs_found = 0;
++
++      if (rootfs_found)
++              return;
++
++      if (of_find_property(mtd_get_of_node(part), "linux,rootfs", NULL) ||
++          !strcmp(part->name, "rootfs")) {
++              run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
++
++              rootfs_found = 1;
++      }
++
++      if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
++          !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
++          !of_find_property(mtd_get_of_node(part), "compatible", NULL))
++              split_firmware(master, part);
++}
++
+ int mtd_add_partition(struct mtd_info *parent, const char *name,
+                     long long offset, long long length)
+ {
+@@ -280,6 +423,7 @@ int mtd_add_partition(struct mtd_info *p
+       if (ret)
+               goto err_remove_part;
++      mtd_partition_split(parent, child);
+       mtd_add_partition_attrs(child);
+       return 0;
+@@ -423,6 +567,7 @@ int add_mtd_partitions(struct mtd_info *
+                       goto err_del_partitions;
+               }
++              mtd_partition_split(master, child);
+               mtd_add_partition_attrs(child);
+               /* Look for subpartitions */
+@@ -439,31 +584,6 @@ err_del_partitions:
+       return ret;
+ }
+-static DEFINE_SPINLOCK(part_parser_lock);
+-static LIST_HEAD(part_parsers);
+-
+-static struct mtd_part_parser *mtd_part_parser_get(const char *name)
+-{
+-      struct mtd_part_parser *p, *ret = NULL;
+-
+-      spin_lock(&part_parser_lock);
+-
+-      list_for_each_entry(p, &part_parsers, list)
+-              if (!strcmp(p->name, name) && try_module_get(p->owner)) {
+-                      ret = p;
+-                      break;
+-              }
+-
+-      spin_unlock(&part_parser_lock);
+-
+-      return ret;
+-}
+-
+-static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
+-{
+-      module_put(p->owner);
+-}
+-
+ /*
+  * Many partition parsers just expected the core to kfree() all their data in
+  * one chunk. Do that by default.
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -615,6 +615,24 @@ static inline void mtd_align_erase_req(s
+               req->len += mtd->erasesize - mod;
+ }
++static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
++{
++      if (mtd_mod_by_eb(sz, mtd) == 0)
++              return sz;
++
++      /* Round up to next erase block */
++      return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
++}
++
++static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
++{
++      if (mtd_mod_by_eb(sz, mtd) == 0)
++              return sz;
++
++      /* Round down to the start of the current erase block */
++      return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
++}
++
+ static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
+ {
+       if (mtd->writesize_shift)
+@@ -688,6 +706,13 @@ extern struct mtd_info *of_get_mtd_devic
+ extern struct mtd_info *get_mtd_device_nm(const char *name);
+ extern void put_mtd_device(struct mtd_info *mtd);
++static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
++{
++      if (!mtd_is_partition(mtd))
++              return 0;
++
++      return mtd->part.offset;
++}
+ struct mtd_notifier {
+       void (*add)(struct mtd_info *mtd);
+--- a/include/linux/mtd/partitions.h
++++ b/include/linux/mtd/partitions.h
+@@ -75,6 +75,12 @@ struct mtd_part_parser_data {
+  * Functions dealing with the various ways of partitioning the space
+  */
++enum mtd_parser_type {
++      MTD_PARSER_TYPE_DEVICE = 0,
++      MTD_PARSER_TYPE_ROOTFS,
++      MTD_PARSER_TYPE_FIRMWARE,
++};
++
+ struct mtd_part_parser {
+       struct list_head list;
+       struct module *owner;
+@@ -83,6 +89,7 @@ struct mtd_part_parser {
+       int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
+                       struct mtd_part_parser_data *);
+       void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
++      enum mtd_parser_type type;
+ };
+ /* Container for passing around a set of parsed partitions */
diff --git a/target/linux/generic/pending-6.6/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch b/target/linux/generic/pending-6.6/401-mtd-don-t-register-NVMEM-devices-for-partitions-with.patch
new file mode 100644 (file)
index 0000000..54a02d8
--- /dev/null
@@ -0,0 +1,48 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 31 Oct 2023 15:51:01 +0100
+Subject: [PATCH] mtd: don't register NVMEM devices for partitions with custom
+ drivers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This fixes issue exposed by upstream commit f4cf4e5db331 ("Revert
+"nvmem: add new config option"").
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+ drivers/mtd/mtdcore.c | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+--- a/drivers/mtd/mtdcore.c
++++ b/drivers/mtd/mtdcore.c
+@@ -548,6 +548,29 @@ static int mtd_nvmem_add(struct mtd_info
+       struct device_node *node = mtd_get_of_node(mtd);
+       struct nvmem_config config = {};
++      /*
++       * Do NOT register NVMEM device for any partition that is meant to be
++       * handled by a U-Boot env driver. That would result in associating two
++       * different NVMEM devices with the same OF node.
++       *
++       * An example of unwanted behaviour of above (forwardtrace):
++       * of_get_mac_addr_nvmem()
++       * of_nvmem_cell_get()
++       * __nvmem_device_get()
++       *
++       * We can't have __nvmem_device_get() return "mtdX" NVMEM device instead
++       * of U-Boot env NVMEM device. That would result in failing to find
++       * NVMEM cell.
++       *
++       * This issue seems to affect U-Boot env case only and will go away with
++       * switch to NVMEM layouts.
++       */
++      if (of_device_is_compatible(node, "u-boot,env") ||
++          of_device_is_compatible(node, "u-boot,env-redundant-bool") ||
++          of_device_is_compatible(node, "u-boot,env-redundant-count") ||
++          of_device_is_compatible(node, "brcm,env"))
++              return 0;
++
+       config.id = NVMEM_DEVID_NONE;
+       config.dev = &mtd->dev;
+       config.name = dev_name(&mtd->dev);
diff --git a/target/linux/generic/pending-6.6/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch b/target/linux/generic/pending-6.6/402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch
new file mode 100644 (file)
index 0000000..5a812b8
--- /dev/null
@@ -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
+@@ -198,6 +198,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)
+ {
+@@ -343,6 +352,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
+@@ -47,6 +47,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;
+@@ -171,6 +172,7 @@ static struct mtd_info *allocate_partiti
+       } else {
+               /* Single erase size */
+               child->erasesize = master->erasesize;
++              child->erasesize_minor = master->erasesize_minor;
+       }
+       /*
+@@ -178,26 +180,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
+@@ -1150,6 +1150,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;
+ }
+@@ -2582,6 +2584,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;
+@@ -2614,8 +2617,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];
+               }
+       }
+@@ -2623,6 +2627,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
+@@ -245,6 +245,8 @@ struct mtd_info {
+        * information below if they desire
+        */
+       uint32_t erasesize;
++      /* "Minor" (smallest) erase size supported by the whole device */
++      uint32_t erasesize_minor;
+       /* Minimal writable flash unit size. In case of NOR flash it is 1 (even
+        * though individual bits can be cleared), in case of NAND flash it is
+        * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
diff --git a/target/linux/generic/pending-6.6/410-mtd-spinand-set-bitflip_threshold-to-75-of-ECC-strength.patch b/target/linux/generic/pending-6.6/410-mtd-spinand-set-bitflip_threshold-to-75-of-ECC-strength.patch
new file mode 100644 (file)
index 0000000..84c6c4a
--- /dev/null
@@ -0,0 +1,63 @@
+From patchwork Mon Aug 12 01:56:41 2024
+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: 1971406
+Return-Path: 
+ <linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org>
+X-Original-To: incoming@patchwork.ozlabs.org
+Delivered-To: patchwork-incoming@legolas.ozlabs.org
+Date: Mon, 12 Aug 2024 02:56:41 +0100
+From: Daniel Golle <daniel@makrotopia.org>
+To: Miquel Raynal <miquel.raynal@bootlin.com>,
+       Richard Weinberger <richard@nod.at>,
+       Vignesh Raghavendra <vigneshr@ti.com>,
+       Tudor Ambarus <tudor.ambarus@linaro.org>,
+       Daniel Golle <daniel@makrotopia.org>,
+       Mika Westerberg <mika.westerberg@linux.intel.com>,
+       Chia-Lin Kao <acelan.kao@canonical.com>,
+       Martin Kurbanov <mmkurbanov@salutedevices.com>,
+       linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org
+Subject: [PATCH] mtd: spinand: set bitflip_threshold to 75% of ECC strength
+Message-ID: 
+ <2117e387260b0a96f95b8e1652ff79e0e2d71d53.1723427450.git.daniel@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+X-BeenThere: linux-mtd@lists.infradead.org
+X-Mailman-Version: 2.1.34
+Precedence: list
+List-Id: Linux MTD discussion mailing list <linux-mtd.lists.infradead.org>
+List-Unsubscribe: <http://lists.infradead.org/mailman/options/linux-mtd>,
+ <mailto:linux-mtd-request@lists.infradead.org?subject=unsubscribe>
+List-Archive: <http://lists.infradead.org/pipermail/linux-mtd/>
+List-Post: <mailto:linux-mtd@lists.infradead.org>
+List-Help: <mailto:linux-mtd-request@lists.infradead.org?subject=help>
+List-Subscribe: <http://lists.infradead.org/mailman/listinfo/linux-mtd>,
+ <mailto:linux-mtd-request@lists.infradead.org?subject=subscribe>
+Sender: "linux-mtd" <linux-mtd-bounces@lists.infradead.org>
+Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org
+
+Reporting an unclean read from SPI-NAND only when the maximum number
+of correctable bitflip errors has been hit seems a bit late.
+UBI LEB scrubbing, which depends on the lower MTD device reporting
+correctable bitflips, then only kicks in when it's almost too late.
+
+Set bitflip_threshold to 75% of the ECC strength, which is also the
+default for raw NAND.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mtd/nand/spi/core.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1287,6 +1287,7 @@ static int spinand_init(struct spinand_d
+       /* Propagate ECC information to mtd_info */
+       mtd->ecc_strength = nanddev_get_ecc_conf(nand)->strength;
+       mtd->ecc_step_size = nanddev_get_ecc_conf(nand)->step_size;
++      mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
+       ret = spinand_create_dirmaps(spinand);
+       if (ret) {
diff --git a/target/linux/generic/pending-6.6/420-mtd-redboot_space.patch b/target/linux/generic/pending-6.6/420-mtd-redboot_space.patch
new file mode 100644 (file)
index 0000000..5518ea7
--- /dev/null
@@ -0,0 +1,41 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: add patch for including unpartitioned space in the rootfs partition for redboot devices (if applicable)
+
+[john@phrozen.org: used by ixp and others]
+
+lede-commit: 394918851f84e4d00fa16eb900e7700e95091f00
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/redboot.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+--- a/drivers/mtd/parsers/redboot.c
++++ b/drivers/mtd/parsers/redboot.c
+@@ -278,14 +278,21 @@ nogood:
+ #endif
+               names += strlen(names) + 1;
+-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+               if (fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
+-                      i++;
+-                      parts[i].offset = parts[i - 1].size + parts[i - 1].offset;
+-                      parts[i].size = fl->next->img->flash_base - parts[i].offset;
+-                      parts[i].name = nullname;
+-              }
++                      if (!strcmp(parts[i].name, "rootfs")) {
++                              parts[i].size = fl->next->img->flash_base;
++                              parts[i].size &= ~(master->erasesize - 1);
++                              parts[i].size -= parts[i].offset;
++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
++                              nrparts--;
++                      } else {
++                              i++;
++                              parts[i].offset = parts[i-1].size + parts[i-1].offset;
++                              parts[i].size = fl->next->img->flash_base - parts[i].offset;
++                              parts[i].name = nullname;
+ #endif
++                      }
++              }
+               tmp_fl = fl;
+               fl = fl->next;
+               kfree(tmp_fl);
diff --git a/target/linux/generic/pending-6.6/430-mtd-add-myloader-partition-parser.patch b/target/linux/generic/pending-6.6/430-mtd-add-myloader-partition-parser.patch
new file mode 100644 (file)
index 0000000..35e80d6
--- /dev/null
@@ -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
+@@ -67,6 +67,22 @@ config MTD_CMDLINE_PARTS
+         If unsure, say 'N'.
++config MTD_MYLOADER_PARTS
++      tristate "MyLoader partition parsing"
++      depends on ADM5120 || 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
+@@ -4,6 +4,7 @@ obj-$(CONFIG_MTD_BCM47XX_PARTS)                += bcm4
+ obj-$(CONFIG_MTD_BCM63XX_PARTS)               += bcm63xxpart.o
+ obj-$(CONFIG_MTD_BRCM_U_BOOT)         += brcm_u-boot.o
+ obj-$(CONFIG_MTD_CMDLINE_PARTS)               += cmdlinepart.o
++obj-$(CONFIG_MTD_MYLOADER_PARTS)              += myloader.o
+ obj-$(CONFIG_MTD_OF_PARTS)            += ofpart.o
+ ofpart-y                              += ofpart_core.o
+ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o
+--- /dev/null
++++ b/drivers/mtd/parsers/myloader.c
+@@ -0,0 +1,181 @@
++/*
++ *  Parse MyLoader-style flash partition tables and produce a Linux partition
++ *  array to match.
++ *
++ *  Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
++ *
++ *  This file was based on drivers/mtd/redboot.c
++ *  Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>
++ *
++ *  This program is free software; you can redistribute it and/or modify it
++ *  under the terms of the GNU General Public License version 2 as published
++ *  by the Free Software Foundation.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/vmalloc.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/byteorder/generic.h>
++#include <linux/myloader.h>
++
++#define BLOCK_LEN_MIN         0x10000
++#define PART_NAME_LEN         32
++
++struct part_data {
++      struct mylo_partition_table     tab;
++      char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN];
++};
++
++static int myloader_parse_partitions(struct mtd_info *master,
++                                   const struct mtd_partition **pparts,
++                                   struct mtd_part_parser_data *data)
++{
++      struct part_data *buf;
++      struct mylo_partition_table *tab;
++      struct mylo_partition *part;
++      struct mtd_partition *mtd_parts;
++      struct mtd_partition *mtd_part;
++      int num_parts;
++      int ret, i;
++      size_t retlen;
++      char *names;
++      unsigned long offset;
++      unsigned long blocklen;
++
++      buf = vmalloc(sizeof(*buf));
++      if (!buf) {
++              return -ENOMEM;
++              goto out;
++      }
++      tab = &buf->tab;
++
++      blocklen = master->erasesize;
++      if (blocklen < BLOCK_LEN_MIN)
++              blocklen = BLOCK_LEN_MIN;
++
++      offset = blocklen;
++
++      /* Find the partition table */
++      for (i = 0; i < 4; i++, offset += blocklen) {
++              printk(KERN_DEBUG "%s: searching for MyLoader partition table"
++                              " at offset 0x%lx\n", master->name, offset);
++
++              ret = mtd_read(master, offset, sizeof(*buf), &retlen,
++                             (void *)buf);
++              if (ret)
++                      goto out_free_buf;
++
++              if (retlen != sizeof(*buf)) {
++                      ret = -EIO;
++                      goto out_free_buf;
++              }
++
++              /* Check for Partition Table magic number */
++              if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS))
++                      break;
++
++      }
++
++      if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) {
++              printk(KERN_DEBUG "%s: no MyLoader partition table found\n",
++                      master->name);
++              ret = 0;
++              goto out_free_buf;
++      }
++
++      /* The MyLoader and the Partition Table is always present */
++      num_parts = 2;
++
++      /* Detect number of used partitions */
++      for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
++              part = &tab->partitions[i];
++
++              if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
++                      continue;
++
++              num_parts++;
++      }
++
++      mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) +
++                              num_parts * PART_NAME_LEN), GFP_KERNEL);
++
++      if (!mtd_parts) {
++              ret = -ENOMEM;
++              goto out_free_buf;
++      }
++
++      mtd_part = mtd_parts;
++      names = (char *)&mtd_parts[num_parts];
++
++      strncpy(names, "myloader", PART_NAME_LEN);
++      mtd_part->name = names;
++      mtd_part->offset = 0;
++      mtd_part->size = offset;
++      mtd_part->mask_flags = MTD_WRITEABLE;
++      mtd_part++;
++      names += PART_NAME_LEN;
++
++      strncpy(names, "partition_table", PART_NAME_LEN);
++      mtd_part->name = names;
++      mtd_part->offset = offset;
++      mtd_part->size = blocklen;
++      mtd_part->mask_flags = MTD_WRITEABLE;
++      mtd_part++;
++      names += PART_NAME_LEN;
++
++      for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
++              part = &tab->partitions[i];
++
++              if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
++                      continue;
++
++              if ((buf->names[i][0]) && (buf->names[i][0] != '\xff'))
++                      strncpy(names, buf->names[i], PART_NAME_LEN);
++              else
++                      snprintf(names, PART_NAME_LEN, "partition%d", i);
++
++              mtd_part->offset = le32_to_cpu(part->addr);
++              mtd_part->size = le32_to_cpu(part->size);
++              mtd_part->name = names;
++              mtd_part++;
++              names += PART_NAME_LEN;
++      }
++
++      *pparts = mtd_parts;
++      ret = num_parts;
++
++ out_free_buf:
++      vfree(buf);
++ out:
++      return ret;
++}
++
++static struct mtd_part_parser myloader_mtd_parser = {
++      .owner          = THIS_MODULE,
++      .parse_fn       = myloader_parse_partitions,
++      .name           = "MyLoader",
++};
++
++static int __init myloader_mtd_parser_init(void)
++{
++      register_mtd_parser(&myloader_mtd_parser);
++
++      return 0;
++}
++
++static void __exit myloader_mtd_parser_exit(void)
++{
++      deregister_mtd_parser(&myloader_mtd_parser);
++}
++
++module_init(myloader_mtd_parser_init);
++module_exit(myloader_mtd_parser_exit);
++
++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
++MODULE_DESCRIPTION("Parsing code for MyLoader partition tables");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/pending-6.6/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch b/target/linux/generic/pending-6.6/431-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch
new file mode 100644 (file)
index 0000000..bcea45d
--- /dev/null
@@ -0,0 +1,68 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Subject: [PATCH] mtd: bcm47xxpart: check for bad blocks when calculating offsets
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+
+--- a/drivers/mtd/parsers/parser_trx.c
++++ b/drivers/mtd/parsers/parser_trx.c
+@@ -25,6 +25,33 @@ struct trx_header {
+       uint32_t offset[3];
+ } __packed;
++/*
++ * Calculate real end offset (address) for a given amount of data. It checks
++ * all blocks skipping bad ones.
++ */
++static size_t parser_trx_real_offset(struct mtd_info *mtd, size_t bytes)
++{
++      size_t real_offset = 0;
++
++      if (mtd_block_isbad(mtd, real_offset))
++              pr_warn("Base offset shouldn't be at bad block");
++
++      while (bytes >= mtd->erasesize) {
++              bytes -= mtd->erasesize;
++              real_offset += mtd->erasesize;
++              while (mtd_block_isbad(mtd, real_offset)) {
++                      real_offset += mtd->erasesize;
++
++                      if (real_offset >= mtd->size)
++                              return real_offset - mtd->erasesize;
++              }
++      }
++
++      real_offset += bytes;
++
++      return real_offset;
++}
++
+ static const char *parser_trx_data_part_name(struct mtd_info *master,
+                                            size_t offset)
+ {
+@@ -86,21 +113,21 @@ static int parser_trx_parse(struct mtd_i
+       if (trx.offset[2]) {
+               part = &parts[curr_part++];
+               part->name = "loader";
+-              part->offset = trx.offset[i];
++              part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
+               i++;
+       }
+       if (trx.offset[i]) {
+               part = &parts[curr_part++];
+               part->name = "linux";
+-              part->offset = trx.offset[i];
++              part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
+               i++;
+       }
+       if (trx.offset[i]) {
+               part = &parts[curr_part++];
+-              part->name = parser_trx_data_part_name(mtd, trx.offset[i]);
+-              part->offset = trx.offset[i];
++              part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
++              part->name = parser_trx_data_part_name(mtd, part->offset);
+               i++;
+       }
diff --git a/target/linux/generic/pending-6.6/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch b/target/linux/generic/pending-6.6/432-mtd-bcm47xxpart-detect-T_Meter-partition.patch
new file mode 100644 (file)
index 0000000..852654d
--- /dev/null
@@ -0,0 +1,37 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Subject: mtd: bcm47xxpart: detect T_Meter partition
+
+It can be found on many Netgear devices. It consists of many 0x30 blocks
+starting with 4D 54.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+ drivers/mtd/bcm47xxpart.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/parsers/bcm47xxpart.c
++++ b/drivers/mtd/parsers/bcm47xxpart.c
+@@ -35,6 +35,7 @@
+ #define NVRAM_HEADER                  0x48534C46      /* FLSH */
+ #define POT_MAGIC1                    0x54544f50      /* POTT */
+ #define POT_MAGIC2                    0x504f          /* OP */
++#define T_METER_MAGIC                 0x4D540000      /* MT */
+ #define ML_MAGIC1                     0x39685a42
+ #define ML_MAGIC2                     0x26594131
+ #define TRX_MAGIC                     0x30524448
+@@ -178,6 +179,15 @@ static int bcm47xxpart_parse(struct mtd_
+                                            MTD_WRITEABLE);
+                       continue;
+               }
++
++              /* T_Meter */
++              if ((le32_to_cpu(buf[0x000 / 4]) & 0xFFFF0000) == T_METER_MAGIC &&
++                  (le32_to_cpu(buf[0x030 / 4]) & 0xFFFF0000) == T_METER_MAGIC &&
++                  (le32_to_cpu(buf[0x060 / 4]) & 0xFFFF0000) == T_METER_MAGIC) {
++                      bcm47xxpart_add_part(&parts[curr_part++], "T_Meter", offset,
++                                           MTD_WRITEABLE);
++                      continue;
++              }
+               /* TRX */
+               if (buf[0x000 / 4] == TRX_MAGIC) {
diff --git a/target/linux/generic/pending-6.6/435-mtd-add-routerbootpart-parser-config.patch b/target/linux/generic/pending-6.6/435-mtd-add-routerbootpart-parser-config.patch
new file mode 100644 (file)
index 0000000..a42dcc8
--- /dev/null
@@ -0,0 +1,38 @@
+From 4437e01fb6bca63fccdba5d6c44888b0935885c2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org>
+Date: Tue, 24 Mar 2020 11:45:07 +0100
+Subject: [PATCH] generic: routerboot partition build bits (5.4)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds routerbootpart kernel build bits
+
+Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
+---
+ drivers/mtd/parsers/Kconfig  | 9 +++++++++
+ drivers/mtd/parsers/Makefile | 1 +
+ 2 files changed, 10 insertions(+)
+
+--- a/drivers/mtd/parsers/Kconfig
++++ b/drivers/mtd/parsers/Kconfig
+@@ -236,3 +236,12 @@ config MTD_SERCOMM_PARTS
+         partition map. This partition table contains real partition
+         offsets, which may differ from device to device depending on the
+         number and location of bad blocks on NAND.
++
++config MTD_ROUTERBOOT_PARTS
++      tristate "RouterBoot flash partition parser"
++      depends on MTD && OF
++      help
++       MikroTik RouterBoot is implemented as a multi segment system on the
++       flash, some of which are fixed and some of which are located at
++       variable offsets. This parser handles both cases via properly
++       formatted DTS.
+--- a/drivers/mtd/parsers/Makefile
++++ b/drivers/mtd/parsers/Makefile
+@@ -17,3 +17,4 @@ obj-$(CONFIG_MTD_SERCOMM_PARTS)              += scpa
+ obj-$(CONFIG_MTD_SHARPSL_PARTS)               += sharpslpart.o
+ obj-$(CONFIG_MTD_REDBOOT_PARTS)               += redboot.o
+ obj-$(CONFIG_MTD_QCOMSMEM_PARTS)      += qcomsmempart.o
++obj-$(CONFIG_MTD_ROUTERBOOT_PARTS)            += routerbootpart.o
diff --git a/target/linux/generic/pending-6.6/450-dt-bindings-block-add-basic-bindings-for-block-devic.patch b/target/linux/generic/pending-6.6/450-dt-bindings-block-add-basic-bindings-for-block-devic.patch
new file mode 100644 (file)
index 0000000..9089ce6
--- /dev/null
@@ -0,0 +1,120 @@
+From 3245921a87154bdfbe7a55d743ea62dd559a8fb0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 30 May 2024 03:13:09 +0100
+Subject: [PATCH 1/9] dt-bindings: block: add basic bindings for block devices
+
+Add bindings for block devices which are used to allow referencing
+nvmem bits on them.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ .../bindings/block/block-device.yaml          | 22 ++++++++
+ .../devicetree/bindings/block/partition.yaml  | 51 +++++++++++++++++++
+ .../devicetree/bindings/block/partitions.yaml | 20 ++++++++
+ 3 files changed, 93 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/block/block-device.yaml
+ create mode 100644 Documentation/devicetree/bindings/block/partition.yaml
+ create mode 100644 Documentation/devicetree/bindings/block/partitions.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/block/block-device.yaml
+@@ -0,0 +1,22 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/block/block-device.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: block storage device
++
++description: |
++  This binding is generic and describes a block-oriented storage device.
++
++maintainers:
++  - Daniel Golle <daniel@makrotopia.org>
++
++properties:
++  partitions:
++    $ref: /schemas/block/partitions.yaml
++
++  nvmem-layout:
++    $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
++
++unevaluatedProperties: false
+--- /dev/null
++++ b/Documentation/devicetree/bindings/block/partition.yaml
+@@ -0,0 +1,51 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/block/partition.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Partition on a block device
++
++description: |
++  This binding describes a partition on a block device.
++  Partitions may be matched by a combination of partition number, name,
++  and UUID.
++
++maintainers:
++  - Daniel Golle <daniel@makrotopia.org>
++
++properties:
++  $nodename:
++    pattern: '^block-partition-.+$'
++
++  partnum:
++    $ref: /schemas/types.yaml#/definitions/uint32
++    description:
++      Matches partition by number if present.
++
++  partname:
++    $ref: /schemas/types.yaml#/definitions/string
++    description:
++      Matches partition by PARTNAME if present.
++
++  partuuid:
++    $ref: /schemas/types.yaml#/definitions/string
++    description:
++      Matches partition by PARTUUID if present.
++
++  nvmem-layout:
++    $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
++    description:
++      This container may reference an NVMEM layout parser.
++
++anyOf:
++  - required:
++      - partnum
++
++  - required:
++      - partname
++
++  - required:
++      - partuuid
++
++unevaluatedProperties: false
+--- /dev/null
++++ b/Documentation/devicetree/bindings/block/partitions.yaml
+@@ -0,0 +1,20 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/block/partitions.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Partitions on block devices
++
++description: |
++  This binding is generic and describes the content of the partitions container
++  node.
++
++maintainers:
++  - Daniel Golle <daniel@makrotopia.org>
++
++patternProperties:
++  "^block-partition-.+$":
++    $ref: partition.yaml
++
++unevaluatedProperties: false
diff --git a/target/linux/generic/pending-6.6/451-block-partitions-populate-fwnode.patch b/target/linux/generic/pending-6.6/451-block-partitions-populate-fwnode.patch
new file mode 100644 (file)
index 0000000..35b908c
--- /dev/null
@@ -0,0 +1,135 @@
+From patchwork Tue Jul 30 19:25:59 2024
+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: 13747816
+Date: Tue, 30 Jul 2024 20:25:59 +0100
+From: Daniel Golle <daniel@makrotopia.org>
+To: Rob Herring <robh@kernel.org>, Krzysztof Kozlowski <krzk+dt@kernel.org>,
+ Conor Dooley <conor+dt@kernel.org>, Jens Axboe <axboe@kernel.dk>,
+ Daniel Golle <daniel@makrotopia.org>, Christian Brauner <brauner@kernel.org>,
+ Al Viro <viro@zeniv.linux.org.uk>, Li Lingfeng <lilingfeng3@huawei.com>,
+ Ming Lei <ming.lei@redhat.com>, Christian Heusel <christian@heusel.eu>,
+       =?utf-8?b?UmFmYcWCIE1pxYJlY2tp?= <rafal@milecki.pl>,
+ Felix Fietkau <nbd@nbd.name>, John Crispin <john@phrozen.org>,
+ Chad Monroe <chad.monroe@adtran.com>, Yangyu Chen <cyy@cyyself.name>,
+ Tianling Shen <cnsztl@immortalwrt.org>, Chuanhong Guo <gch981213@gmail.com>,
+ Chen Minqiang <ptpt52@gmail.com>, devicetree@vger.kernel.org,
+ linux-kernel@vger.kernel.org, linux-block@vger.kernel.org
+Subject: [PATCH v5 2/4] block: partitions: populate fwnode
+Message-ID: 
+ <3051ac090ad3b3e2f5adb6b67c923261ead729a5.1722365899.git.daniel@makrotopia.org>
+References: <cover.1722365899.git.daniel@makrotopia.org>
+Precedence: bulk
+X-Mailing-List: linux-block@vger.kernel.org
+List-Id: <linux-block.vger.kernel.org>
+List-Subscribe: <mailto:linux-block+subscribe@vger.kernel.org>
+List-Unsubscribe: <mailto:linux-block+unsubscribe@vger.kernel.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+In-Reply-To: <cover.1722365899.git.daniel@makrotopia.org>
+
+Assign matching firmware nodes to block partitions in order to allow
+them to be referenced e.g. as NVMEM providers.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ block/partitions/core.c | 72 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 72 insertions(+)
+
+--- a/block/partitions/core.c
++++ b/block/partitions/core.c
+@@ -10,6 +10,8 @@
+ #include <linux/ctype.h>
+ #include <linux/vmalloc.h>
+ #include <linux/raid/detect.h>
++#include <linux/property.h>
++
+ #include "check.h"
+ static int (*const check_part[])(struct parsed_partitions *) = {
+@@ -295,6 +297,74 @@ static ssize_t whole_disk_show(struct de
+ }
+ static const DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL);
++static bool part_meta_match(const char *attr, const char *member, size_t length)
++{
++      /* check if length of attr exceeds specified maximum length */
++      if (strnlen(attr, length) == length)
++              return false;
++
++      /* return true if strings match */
++      return !strncmp(attr, member, length);
++}
++
++static struct fwnode_handle *find_partition_fwnode(struct block_device *bdev)
++{
++      struct fwnode_handle *fw_parts, *fw_part;
++      struct device *ddev = disk_to_dev(bdev->bd_disk);
++      const char *partname, *uuid;
++      u32 partno;
++      bool got_uuid, got_partname, got_partno;
++
++      fw_parts = device_get_named_child_node(ddev, "partitions");
++      if (!fw_parts)
++              return NULL;
++
++      fwnode_for_each_child_node(fw_parts, fw_part) {
++              got_uuid = false;
++              got_partname = false;
++              got_partno = false;
++              /*
++               * In case 'uuid' is defined in the partitions firmware node
++               * require partition meta info being present and the specified
++               * uuid to match.
++               */
++              got_uuid = !fwnode_property_read_string(fw_part, "uuid", &uuid);
++              if (got_uuid && (!bdev->bd_meta_info ||
++                               !part_meta_match(uuid, bdev->bd_meta_info->uuid,
++                                                PARTITION_META_INFO_UUIDLTH)))
++                      continue;
++
++              /*
++               * In case 'partname' is defined in the partitions firmware node
++               * require partition meta info being present and the specified
++               * volname to match.
++               */
++              got_partname = !fwnode_property_read_string(fw_part, "partname",
++                                                          &partname);
++              if (got_partname && (!bdev->bd_meta_info ||
++                                   !part_meta_match(partname,
++                                                    bdev->bd_meta_info->volname,
++                                                    PARTITION_META_INFO_VOLNAMELTH)))
++                      continue;
++
++              /*
++               * In case 'partno' is defined in the partitions firmware node
++               * the specified partno needs to match.
++               */
++              got_partno = !fwnode_property_read_u32(fw_part, "partno", &partno);
++              if (got_partno && bdev->bd_partno != partno)
++                      continue;
++
++              /* Skip if no matching criteria is present in firmware node */
++              if (!got_uuid && !got_partname && !got_partno)
++                      continue;
++
++              return fw_part;
++      }
++
++      return NULL;
++}
++
+ /*
+  * Must be called either with open_mutex held, before a disk can be opened or
+  * after all disk users are gone.
+@@ -377,6 +447,8 @@ static struct block_device *add_partitio
+                       goto out_put;
+       }
++      device_set_node(pdev, find_partition_fwnode(bdev));
++
+       /* delay uevent until 'holders' subdir is created */
+       dev_set_uevent_suppress(pdev, 1);
+       err = device_add(pdev);
diff --git a/target/linux/generic/pending-6.6/452-block-add-support-for-notifications.patch b/target/linux/generic/pending-6.6/452-block-add-support-for-notifications.patch
new file mode 100644 (file)
index 0000000..667852e
--- /dev/null
@@ -0,0 +1,174 @@
+From patchwork Tue Jul 30 19:26:42 2024
+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: 13747817
+Date: Tue, 30 Jul 2024 20:26:42 +0100
+From: Daniel Golle <daniel@makrotopia.org>
+To: Rob Herring <robh@kernel.org>, Krzysztof Kozlowski <krzk+dt@kernel.org>,
+ Conor Dooley <conor+dt@kernel.org>, Jens Axboe <axboe@kernel.dk>,
+ Daniel Golle <daniel@makrotopia.org>, Christian Brauner <brauner@kernel.org>,
+ Al Viro <viro@zeniv.linux.org.uk>, Li Lingfeng <lilingfeng3@huawei.com>,
+ Ming Lei <ming.lei@redhat.com>, Christian Heusel <christian@heusel.eu>,
+       =?utf-8?b?UmFmYcWCIE1pxYJlY2tp?= <rafal@milecki.pl>,
+ Felix Fietkau <nbd@nbd.name>, John Crispin <john@phrozen.org>,
+ Chad Monroe <chad.monroe@adtran.com>, Yangyu Chen <cyy@cyyself.name>,
+ Tianling Shen <cnsztl@immortalwrt.org>, Chuanhong Guo <gch981213@gmail.com>,
+ Chen Minqiang <ptpt52@gmail.com>, devicetree@vger.kernel.org,
+ linux-kernel@vger.kernel.org, linux-block@vger.kernel.org
+Subject: [PATCH v5 3/4] block: add support for notifications
+Message-ID: 
+ <ca0022886e8f211a323a716653a1396a3bc91653.1722365899.git.daniel@makrotopia.org>
+References: <cover.1722365899.git.daniel@makrotopia.org>
+Precedence: bulk
+X-Mailing-List: linux-block@vger.kernel.org
+List-Id: <linux-block.vger.kernel.org>
+List-Subscribe: <mailto:linux-block+subscribe@vger.kernel.org>
+List-Unsubscribe: <mailto:linux-block+unsubscribe@vger.kernel.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+In-Reply-To: <cover.1722365899.git.daniel@makrotopia.org>
+
+Add notifier block to notify other subsystems about the addition or
+removal of block devices.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ block/Kconfig          |  6 +++
+ block/Makefile         |  1 +
+ block/blk-notify.c     | 87 ++++++++++++++++++++++++++++++++++++++++++
+ include/linux/blkdev.h | 11 ++++++
+ 4 files changed, 105 insertions(+)
+ create mode 100644 block/blk-notify.c
+
+--- a/block/Kconfig
++++ b/block/Kconfig
+@@ -208,6 +208,12 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
+         by falling back to the kernel crypto API when inline
+         encryption hardware is not present.
++config BLOCK_NOTIFIERS
++      bool "Enable support for notifications in block layer"
++      help
++        Enable this option to provide notifiers for other subsystems
++        upon addition or removal of block devices.
++
+ source "block/partitions/Kconfig"
+ config BLK_MQ_PCI
+--- a/block/Makefile
++++ b/block/Makefile
+@@ -40,3 +40,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION)  += b
+                                          blk-crypto-sysfs.o
+ obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK)  += blk-crypto-fallback.o
+ obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o
++obj-$(CONFIG_BLOCK_NOTIFIERS) += blk-notify.o
+--- /dev/null
++++ b/block/blk-notify.c
+@@ -0,0 +1,87 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Notifiers for addition and removal of block devices
++ *
++ * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
++ */
++
++#include <linux/list.h>
++#include <linux/mutex.h>
++#include <linux/notifier.h>
++
++#include "blk.h"
++
++struct blk_device_list {
++      struct device *dev;
++      struct list_head list;
++};
++
++static RAW_NOTIFIER_HEAD(blk_notifier_list);
++static DEFINE_MUTEX(blk_notifier_lock);
++static LIST_HEAD(blk_devices);
++
++void blk_register_notify(struct notifier_block *nb)
++{
++      struct blk_device_list *existing_blkdev;
++
++      mutex_lock(&blk_notifier_lock);
++      raw_notifier_chain_register(&blk_notifier_list, nb);
++
++      list_for_each_entry(existing_blkdev, &blk_devices, list)
++              nb->notifier_call(nb, BLK_DEVICE_ADD, existing_blkdev->dev);
++
++      mutex_unlock(&blk_notifier_lock);
++}
++EXPORT_SYMBOL_GPL(blk_register_notify);
++
++void blk_unregister_notify(struct notifier_block *nb)
++{
++      mutex_lock(&blk_notifier_lock);
++      raw_notifier_chain_unregister(&blk_notifier_list, nb);
++      mutex_unlock(&blk_notifier_lock);
++}
++EXPORT_SYMBOL_GPL(blk_unregister_notify);
++
++static int blk_call_notifier_add(struct device *dev)
++{
++      struct blk_device_list *new_blkdev;
++
++      new_blkdev = kmalloc(sizeof(*new_blkdev), GFP_KERNEL);
++      if (!new_blkdev)
++              return -ENOMEM;
++
++      new_blkdev->dev = dev;
++      mutex_lock(&blk_notifier_lock);
++      list_add_tail(&new_blkdev->list, &blk_devices);
++      raw_notifier_call_chain(&blk_notifier_list, BLK_DEVICE_ADD, dev);
++      mutex_unlock(&blk_notifier_lock);
++      return 0;
++}
++
++static void blk_call_notifier_remove(struct device *dev)
++{
++      struct blk_device_list *old_blkdev, *tmp;
++
++      mutex_lock(&blk_notifier_lock);
++      list_for_each_entry_safe(old_blkdev, tmp, &blk_devices, list) {
++              if (old_blkdev->dev != dev)
++                      continue;
++
++              list_del(&old_blkdev->list);
++              kfree(old_blkdev);
++      }
++      raw_notifier_call_chain(&blk_notifier_list, BLK_DEVICE_REMOVE, dev);
++      mutex_unlock(&blk_notifier_lock);
++}
++
++static struct class_interface blk_notifications_bus_interface __refdata = {
++      .class = &block_class,
++      .add_dev = &blk_call_notifier_add,
++      .remove_dev = &blk_call_notifier_remove,
++};
++
++static int __init blk_notifications_init(void)
++{
++      return class_interface_register(&blk_notifications_bus_interface);
++}
++device_initcall(blk_notifications_init);
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -1567,4 +1567,15 @@ struct io_comp_batch {
+ #define DEFINE_IO_COMP_BATCH(name)    struct io_comp_batch name = { }
++
++#define BLK_DEVICE_ADD                1
++#define BLK_DEVICE_REMOVE     2
++#if defined(CONFIG_BLOCK_NOTIFIERS)
++void blk_register_notify(struct notifier_block *nb);
++void blk_unregister_notify(struct notifier_block *nb);
++#else
++static inline void blk_register_notify(struct notifier_block *nb) { };
++static inline void blk_unregister_notify(struct notifier_block *nb) { };
++#endif
++
+ #endif /* _LINUX_BLKDEV_H */
diff --git a/target/linux/generic/pending-6.6/453-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch b/target/linux/generic/pending-6.6/453-block-add-new-genhd-flag-GENHD_FL_NVMEM.patch
new file mode 100644 (file)
index 0000000..79abcd0
--- /dev/null
@@ -0,0 +1,56 @@
+From patchwork Tue Jul 30 19:27:07 2024
+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: 13747818
+Date: Tue, 30 Jul 2024 20:27:07 +0100
+From: Daniel Golle <daniel@makrotopia.org>
+To: Rob Herring <robh@kernel.org>, Krzysztof Kozlowski <krzk+dt@kernel.org>,
+ Conor Dooley <conor+dt@kernel.org>, Jens Axboe <axboe@kernel.dk>,
+ Daniel Golle <daniel@makrotopia.org>, Christian Brauner <brauner@kernel.org>,
+ Al Viro <viro@zeniv.linux.org.uk>, Li Lingfeng <lilingfeng3@huawei.com>,
+ Ming Lei <ming.lei@redhat.com>, Christian Heusel <christian@heusel.eu>,
+       =?utf-8?b?UmFmYcWCIE1pxYJlY2tp?= <rafal@milecki.pl>,
+ Felix Fietkau <nbd@nbd.name>, John Crispin <john@phrozen.org>,
+ Chad Monroe <chad.monroe@adtran.com>, Yangyu Chen <cyy@cyyself.name>,
+ Tianling Shen <cnsztl@immortalwrt.org>, Chuanhong Guo <gch981213@gmail.com>,
+ Chen Minqiang <ptpt52@gmail.com>, devicetree@vger.kernel.org,
+ linux-kernel@vger.kernel.org, linux-block@vger.kernel.org
+Subject: [PATCH v5 4/4] block: add new genhd flag GENHD_FL_NVMEM
+Message-ID: 
+ <311ea569c23ce14e2896cd3b069dc494c58c49c2.1722365899.git.daniel@makrotopia.org>
+References: <cover.1722365899.git.daniel@makrotopia.org>
+Precedence: bulk
+X-Mailing-List: linux-block@vger.kernel.org
+List-Id: <linux-block.vger.kernel.org>
+List-Subscribe: <mailto:linux-block+subscribe@vger.kernel.org>
+List-Unsubscribe: <mailto:linux-block+unsubscribe@vger.kernel.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+In-Reply-To: <cover.1722365899.git.daniel@makrotopia.org>
+
+Add new flag to destinguish block devices which may act as an NVMEM
+provider.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ include/linux/blkdev.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/include/linux/blkdev.h
++++ b/include/linux/blkdev.h
+@@ -80,11 +80,13 @@ struct partition_meta_info {
+  * ``GENHD_FL_NO_PART``: partition support is disabled.  The kernel will not
+  * scan for partitions from add_disk, and users can't add partitions manually.
+  *
++ * ``GENHD_FL_NVMEM``: the block device should be considered as NVMEM provider.
+  */
+ enum {
+       GENHD_FL_REMOVABLE                      = 1 << 0,
+       GENHD_FL_HIDDEN                         = 1 << 1,
+       GENHD_FL_NO_PART                        = 1 << 2,
++      GENHD_FL_NVMEM                          = 1 << 3,
+ };
+ enum {
diff --git a/target/linux/generic/pending-6.6/454-nvmem-implement-block-NVMEM-provider.patch b/target/linux/generic/pending-6.6/454-nvmem-implement-block-NVMEM-provider.patch
new file mode 100644 (file)
index 0000000..3c08f6d
--- /dev/null
@@ -0,0 +1,260 @@
+From 9703951cdfe868b130e64d6122420396c2807be8 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 30 May 2024 03:15:02 +0100
+Subject: [PATCH 5/9] nvmem: implement block NVMEM provider
+
+On embedded devices using an eMMC it is common that one or more partitions
+on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
+data. Allow referencing any block device or partition in Device Tree to
+allow e.g. Ethernet and Wi-Fi drivers accessing them via the NVMEM layer.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/nvmem/Kconfig  |  11 +++
+ drivers/nvmem/Makefile |   2 +
+ drivers/nvmem/block.c  | 197 +++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 210 insertions(+)
+ create mode 100644 drivers/nvmem/block.c
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -40,6 +40,17 @@ config NVMEM_APPLE_EFUSES
+         This driver can also be built as a module. If so, the module will
+         be called nvmem-apple-efuses.
++config NVMEM_BLOCK
++      tristate "Block device NVMEM provider"
++      depends on BLOCK
++      depends on OF
++      depends on NVMEM
++      select BLOCK_NOTIFIERS
++      help
++        Allow block devices (or partitions) to act as NVMEM prodivers,
++        typically used with eMMC to store MAC addresses or Wi-Fi
++        calibration data on embedded devices.
++
+ config NVMEM_BCM_OCOTP
+       tristate "Broadcom On-Chip OTP Controller support"
+       depends on ARCH_BCM_IPROC || COMPILE_TEST
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -14,6 +14,8 @@ obj-$(CONFIG_NVMEM_APPLE_EFUSES)     += nvme
+ nvmem-apple-efuses-y                  := apple-efuses.o
+ obj-$(CONFIG_NVMEM_BCM_OCOTP)         += nvmem-bcm-ocotp.o
+ nvmem-bcm-ocotp-y                     := bcm-ocotp.o
++obj-$(CONFIG_NVMEM_BLOCK)             += nvmem-block.o
++nvmem-block-y                         := block.o
+ obj-$(CONFIG_NVMEM_BRCM_NVRAM)                += nvmem_brcm_nvram.o
+ nvmem_brcm_nvram-y                    := brcm_nvram.o
+ obj-$(CONFIG_NVMEM_IMX_IIM)           += nvmem-imx-iim.o
+--- /dev/null
++++ b/drivers/nvmem/block.c
+@@ -0,0 +1,208 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * block device NVMEM provider
++ *
++ * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
++ *
++ * Useful on devices using a partition on an eMMC for MAC addresses or
++ * Wi-Fi calibration EEPROM data.
++ */
++
++#include <linux/blkdev.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/pagemap.h>
++#include <linux/property.h>
++
++/* List of all NVMEM devices */
++static LIST_HEAD(nvmem_devices);
++static DEFINE_MUTEX(devices_mutex);
++
++struct blk_nvmem {
++      struct nvmem_device *nvmem;
++      struct block_device *bdev;
++      struct list_head list;
++};
++
++static int blk_nvmem_reg_read(void *priv, unsigned int from,
++                            void *val, size_t bytes)
++{
++      unsigned long offs = from & ~PAGE_MASK, to_read;
++      pgoff_t f_index = from >> PAGE_SHIFT;
++      struct address_space *mapping;
++      struct blk_nvmem *bnv = priv;
++      size_t bytes_left = bytes;
++      struct folio *folio;
++      void *p;
++      int ret;
++
++      if (!bnv->bdev)
++              return -ENODEV;
++
++      if (!bnv->bdev->bd_disk)
++              return -EINVAL;
++
++      if (!bnv->bdev->bd_disk->fops)
++              return -EIO;
++
++      if (!bnv->bdev->bd_disk->fops->open)
++              return -EIO;
++
++      ret = bnv->bdev->bd_disk->fops->open(bnv->bdev->bd_disk, BLK_OPEN_READ);
++      if (ret)
++              return ret;
++
++      mapping = bnv->bdev->bd_inode->i_mapping;
++
++      while (bytes_left) {
++              folio = read_mapping_folio(mapping, f_index++, NULL);
++              if (IS_ERR(folio)) {
++                      ret = PTR_ERR(folio);
++                      goto err_release_bdev;
++              }
++              to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs);
++              p = folio_address(folio) + offset_in_folio(folio, offs);
++              memcpy(val, p, to_read);
++              offs = 0;
++              bytes_left -= to_read;
++              val += to_read;
++              folio_put(folio);
++      }
++
++err_release_bdev:
++      bnv->bdev->bd_disk->fops->release(bnv->bdev->bd_disk);
++
++      return ret;
++}
++
++static int blk_nvmem_register(struct device *dev)
++{
++      struct device_node *np = dev_of_node(dev);
++      struct block_device *bdev = dev_to_bdev(dev);
++      struct nvmem_config config = {};
++      struct blk_nvmem *bnv;
++
++      /* skip devices which do not have a device tree node */
++      if (!np)
++              return 0;
++
++      /* skip devices without an nvmem layout defined */
++      if (!of_get_child_by_name(np, "nvmem-layout"))
++              return 0;
++
++      /*
++       * skip devices which don't have GENHD_FL_NVMEM set
++       *
++       * This flag is used for mtdblock and ubiblock devices because
++       * both, MTD and UBI already implement their own NVMEM provider.
++       * To avoid registering multiple NVMEM providers for the same
++       * device node, don't register the block NVMEM provider for them.
++       */
++      if (!(bdev->bd_disk->flags & GENHD_FL_NVMEM))
++              return 0;
++
++      /*
++       * skip block device too large to be represented as NVMEM devices
++       * which are using an 'int' as address
++       */
++      if (bdev_nr_bytes(bdev) > INT_MAX)
++              return -EFBIG;
++
++      bnv = kzalloc(sizeof(struct blk_nvmem), GFP_KERNEL);
++      if (!bnv)
++              return -ENOMEM;
++
++      config.id = NVMEM_DEVID_NONE;
++      config.dev = &bdev->bd_device;
++      config.name = dev_name(&bdev->bd_device);
++      config.owner = THIS_MODULE;
++      config.priv = bnv;
++      config.reg_read = blk_nvmem_reg_read;
++      config.size = bdev_nr_bytes(bdev);
++      config.word_size = 1;
++      config.stride = 1;
++      config.read_only = true;
++      config.root_only = true;
++      config.ignore_wp = true;
++      config.of_node = to_of_node(dev->fwnode);
++
++      bnv->bdev = bdev;
++      bnv->nvmem = nvmem_register(&config);
++      if (IS_ERR(bnv->nvmem)) {
++              dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem),
++                            "Failed to register NVMEM device\n");
++
++              kfree(bnv);
++              return PTR_ERR(bnv->nvmem);
++      }
++
++      mutex_lock(&devices_mutex);
++      list_add_tail(&bnv->list, &nvmem_devices);
++      mutex_unlock(&devices_mutex);
++
++      return 0;
++}
++
++static void blk_nvmem_unregister(struct device *dev)
++{
++      struct block_device *bdev = dev_to_bdev(dev);
++      struct blk_nvmem *bnv_c, *bnv = NULL;
++
++      mutex_lock(&devices_mutex);
++      list_for_each_entry(bnv_c, &nvmem_devices, list) {
++              if (bnv_c->bdev == bdev) {
++                      bnv = bnv_c;
++                      break;
++              }
++      }
++
++      if (!bnv) {
++              mutex_unlock(&devices_mutex);
++              return;
++      }
++
++      list_del(&bnv->list);
++      mutex_unlock(&devices_mutex);
++      nvmem_unregister(bnv->nvmem);
++      kfree(bnv);
++}
++
++static int blk_nvmem_handler(struct notifier_block *this, unsigned long code, void *obj)
++{
++      struct device *dev = (struct device *)obj;
++
++      switch (code) {
++      case BLK_DEVICE_ADD:
++              return blk_nvmem_register(dev);
++      case BLK_DEVICE_REMOVE:
++              blk_nvmem_unregister(dev);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static struct notifier_block blk_nvmem_notifier = {
++      .notifier_call = blk_nvmem_handler,
++};
++
++static int __init blk_nvmem_init(void)
++{
++      blk_register_notify(&blk_nvmem_notifier);
++
++      return 0;
++}
++
++static void __exit blk_nvmem_exit(void)
++{
++      blk_unregister_notify(&blk_nvmem_notifier);
++}
++
++module_init(blk_nvmem_init);
++module_exit(blk_nvmem_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
++MODULE_DESCRIPTION("block device NVMEM provider");
diff --git a/target/linux/generic/pending-6.6/455-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch b/target/linux/generic/pending-6.6/455-dt-bindings-mmc-mmc-card-add-block-device-nodes.patch
new file mode 100644 (file)
index 0000000..74e6c82
--- /dev/null
@@ -0,0 +1,74 @@
+From f7ec19b34d1b7e934a58ceb102369bbd30b2631d Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 30 May 2024 03:15:11 +0100
+Subject: [PATCH 6/9] dt-bindings: mmc: mmc-card: add block device nodes
+
+Add nodes representing the block devices exposed by an MMC device
+including an example involving nvmem-cells.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ .../devicetree/bindings/mmc/mmc-card.yaml     | 45 +++++++++++++++++++
+ 1 file changed, 45 insertions(+)
+
+--- a/Documentation/devicetree/bindings/mmc/mmc-card.yaml
++++ b/Documentation/devicetree/bindings/mmc/mmc-card.yaml
+@@ -26,6 +26,18 @@ properties:
+       Use this to indicate that the mmc-card has a broken hpi
+       implementation, and that hpi should not be used.
++  block:
++    $ref: /schemas/block/block-device.yaml#
++    description:
++      Represents the block storage provided by an SD card or the
++      main hardware partition of an eMMC.
++
++patternProperties:
++  '^boot[0-9]+':
++    $ref: /schemas/block/block-device.yaml#
++    description:
++      Represents a boot hardware partition on an eMMC.
++
+ required:
+   - compatible
+   - reg
+@@ -42,6 +54,39 @@ examples:
+             compatible = "mmc-card";
+             reg = <0>;
+             broken-hpi;
++
++            block {
++                partitions {
++                    cal_data: block-partition-rf {
++                        partnum = <3>;
++                        partname = "rf";
++
++                        nvmem-layout {
++                            compatible = "fixed-layout";
++                            #address-cells = <1>;
++                            #size-cells = <1>;
++
++                            eeprom@0 {
++                                reg = <0x0 0x1000>;
++                            };
++                        };
++                    };
++                };
++            };
++
++            boot1 {
++                nvmem-layout {
++                    compatible = "fixed-layout";
++                    #address-cells = <1>;
++                    #size-cells = <1>;
++
++                    macaddr: macaddr@a {
++                        compatible = "mac-base";
++                        reg = <0xa 0x6>;
++                        #nvmem-cell-cells = <1>;
++                    };
++                };
++            };
+         };
+     };
diff --git a/target/linux/generic/pending-6.6/456-mmc-core-set-card-fwnode_handle.patch b/target/linux/generic/pending-6.6/456-mmc-core-set-card-fwnode_handle.patch
new file mode 100644 (file)
index 0000000..2ee170d
--- /dev/null
@@ -0,0 +1,23 @@
+From 043c4f88476cc0f29c9bf82a8a516f58d848e1cd Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 30 May 2024 03:15:25 +0100
+Subject: [PATCH 7/9] mmc: core: set card fwnode_handle
+
+Set fwnode in case it isn't set yet and of_node is present.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mmc/core/bus.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/mmc/core/bus.c
++++ b/drivers/mmc/core/bus.c
+@@ -364,6 +364,8 @@ int mmc_add_card(struct mmc_card *card)
+       mmc_add_card_debugfs(card);
+       card->dev.of_node = mmc_of_find_child_device(card->host, 0);
++      if (card->dev.of_node && !card->dev.fwnode)
++              card->dev.fwnode = &card->dev.of_node->fwnode;
+       device_enable_async_suspend(&card->dev);
diff --git a/target/linux/generic/pending-6.6/457-mmc-block-set-fwnode-of-disk-devices.patch b/target/linux/generic/pending-6.6/457-mmc-block-set-fwnode-of-disk-devices.patch
new file mode 100644 (file)
index 0000000..66f3515
--- /dev/null
@@ -0,0 +1,27 @@
+From ef3e38fec26901b71975d7e810a2df6b8bd54a8e Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 30 May 2024 03:15:36 +0100
+Subject: [PATCH 8/9] mmc: block: set fwnode of disk devices
+
+Set fwnode of disk devices to 'block', 'boot0' and 'boot1' subnodes of
+the mmc-card. This is done in preparation for having the eMMC act as
+NVMEM provider.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mmc/core/block.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -2620,6 +2620,10 @@ static struct mmc_blk_data *mmc_blk_allo
+       if (area_type == MMC_BLK_DATA_AREA_MAIN)
+               dev_set_drvdata(&card->dev, md);
+       disk_fwnode = mmc_blk_get_partitions_node(parent, subname);
++      if (!disk_fwnode)
++              disk_fwnode = device_get_named_child_node(subname ? md->parent->parent :
++                                                        md->parent,
++                                                        subname ? subname : "block");
+       ret = add_disk_fwnode(md->parent, md->disk, mmc_disk_attr_groups,
+                             disk_fwnode);
+       if (ret)
diff --git a/target/linux/generic/pending-6.6/458-mmc-block-set-GENHD_FL_NVMEM.patch b/target/linux/generic/pending-6.6/458-mmc-block-set-GENHD_FL_NVMEM.patch
new file mode 100644 (file)
index 0000000..7f9c512
--- /dev/null
@@ -0,0 +1,22 @@
+From 7903b50441000365a6fe5badb39735889f562252 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 30 May 2024 03:15:46 +0100
+Subject: [PATCH 9/9] mmc: block: set GENHD_FL_NVMEM
+
+Set flag to consider MMC block devices as NVMEM providers.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mmc/core/block.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mmc/core/block.c
++++ b/drivers/mmc/core/block.c
+@@ -2566,6 +2566,7 @@ static struct mmc_blk_data *mmc_blk_allo
+       md->disk->major = MMC_BLOCK_MAJOR;
+       md->disk->minors = perdev_minors;
+       md->disk->first_minor = devidx * perdev_minors;
++      md->disk->flags = GENHD_FL_NVMEM;
+       md->disk->fops = &mmc_bdops;
+       md->disk->private_data = md;
+       md->parent = parent;
diff --git a/target/linux/generic/pending-6.6/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch b/target/linux/generic/pending-6.6/460-mtd-cfi_cmdset_0002-no-erase_suspend.patch
new file mode 100644 (file)
index 0000000..2435133
--- /dev/null
@@ -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
+@@ -906,7 +906,7 @@ static int get_chip(struct map_info *map
+               return 0;
+       case FL_ERASING:
+-              if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
++              if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
+                   !(mode == FL_READY || mode == FL_POINT ||
+                   (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
+                       goto sleep;
diff --git a/target/linux/generic/pending-6.6/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch b/target/linux/generic/pending-6.6/461-mtd-cfi_cmdset_0002-add-buffer-write-cmd-timeout.patch
new file mode 100644 (file)
index 0000000..059d967
--- /dev/null
@@ -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
+@@ -2050,6 +2050,7 @@ static int __xipram do_write_buffer(stru
+       /* Write Buffer Load */
+       map_write(map, CMD(0x25), cmd_adr);
++      (void) map_read(map, cmd_adr);
+       chip->state = FL_WRITING_TO_BUFFER;
diff --git a/target/linux/generic/pending-6.6/465-m25p80-mx-disable-software-protection.patch b/target/linux/generic/pending-6.6/465-m25p80-mx-disable-software-protection.patch
new file mode 100644 (file)
index 0000000..09a508b
--- /dev/null
@@ -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
+@@ -114,6 +114,7 @@ static int macronix_nor_late_init(struct
+ {
+       if (!nor->params->set_4byte_addr_mode)
+               nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b;
++      nor->flags |= SNOR_F_HAS_LOCK;
+       return 0;
+ }
diff --git a/target/linux/generic/pending-6.6/476-mtd-spi-nor-add-eon-en25q128.patch b/target/linux/generic/pending-6.6/476-mtd-spi-nor-add-eon-en25q128.patch
new file mode 100644 (file)
index 0000000..303e488
--- /dev/null
@@ -0,0 +1,19 @@
+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
+@@ -17,6 +17,8 @@ static const struct flash_info eon_nor_p
+       { "en25p64",    INFO(0x1c2017, 0, 64 * 1024,  128) },
+       { "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128)
+               NO_SFDP_FLAGS(SECT_4K) },
++      { "en25q128",   INFO(0x1c3018, 0, 64 * 1024,  256)
++              NO_SFDP_FLAGS(SECT_4K) },
+       { "en25q80a",   INFO(0x1c3014, 0, 64 * 1024,   16)
+               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
+       { "en25qh16",   INFO(0x1c7015, 0, 64 * 1024,   32)
diff --git a/target/linux/generic/pending-6.6/477-mtd-spi-nor-add-eon-en25qx128a.patch b/target/linux/generic/pending-6.6/477-mtd-spi-nor-add-eon-en25qx128a.patch
new file mode 100644 (file)
index 0000000..6740d1d
--- /dev/null
@@ -0,0 +1,21 @@
+From: Christian Marangi <ansuelsmth@gmail.com>
+Subject: kernel/mtd: add support for EON EN25QX128A
+
+Add support for EON EN25QX128A with no flags as it does
+support SFDP parsing.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/eon.c
++++ b/drivers/mtd/spi-nor/eon.c
+@@ -19,6 +19,7 @@ static const struct flash_info eon_nor_p
+               NO_SFDP_FLAGS(SECT_4K) },
+       { "en25q128",   INFO(0x1c3018, 0, 64 * 1024,  256)
+               NO_SFDP_FLAGS(SECT_4K) },
++      { "en25qx128a", INFO(0x1c7118, 0, 64 * 1024, 256) },
+       { "en25q80a",   INFO(0x1c3014, 0, 64 * 1024,   16)
+               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
+       { "en25qh16",   INFO(0x1c7015, 0, 64 * 1024,   32)
diff --git a/target/linux/generic/pending-6.6/479-mtd-spi-nor-add-xtx-xt25f128b.patch b/target/linux/generic/pending-6.6/479-mtd-spi-nor-add-xtx-xt25f128b.patch
new file mode 100644 (file)
index 0000000..945f5ba
--- /dev/null
@@ -0,0 +1,81 @@
+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
+ spi-nor-$(CONFIG_DEBUG_FS)    += debugfs.o
+ obj-$(CONFIG_MTD_SPI_NOR)     += spi-nor.o
+--- /dev/null
++++ b/drivers/mtd/spi-nor/xtx.c
+@@ -0,0 +1,17 @@
++// 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)
++              NO_SFDP_FLAGS(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
+@@ -2017,6 +2017,7 @@ static const struct spi_nor_manufacturer
+       &spi_nor_winbond,
+       &spi_nor_xilinx,
+       &spi_nor_xmc,
++      &spi_nor_xtx,
+ };
+ static const struct flash_info spi_nor_generic_flash = {
+--- a/drivers/mtd/spi-nor/core.h
++++ b/drivers/mtd/spi-nor/core.h
+@@ -647,6 +647,7 @@ extern const struct spi_nor_manufacturer
+ extern const struct spi_nor_manufacturer spi_nor_winbond;
+ extern const struct spi_nor_manufacturer spi_nor_xilinx;
+ extern const struct spi_nor_manufacturer spi_nor_xmc;
++extern const struct spi_nor_manufacturer spi_nor_xtx;
+ extern const struct attribute_group *spi_nor_sysfs_groups[];
diff --git a/target/linux/generic/pending-6.6/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch b/target/linux/generic/pending-6.6/481-mtd-spi-nor-add-support-for-Gigadevice-GD25D05.patch
new file mode 100644 (file)
index 0000000..3fdd354
--- /dev/null
@@ -0,0 +1,23 @@
+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
+@@ -34,6 +34,10 @@ static const struct spi_nor_fixups gd25q
+ };
+ static const struct flash_info gigadevice_nor_parts[] = {
++      { "gd25q05", INFO(0xc84010, 0, 64 * 1024,  1)
++              FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++              NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
++                            SPI_NOR_QUAD_READ) },
+       { "gd25q16", INFO(0xc84015, 0, 64 * 1024,  32)
+               FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
diff --git a/target/linux/generic/pending-6.6/482-mtd-spi-nor-add-gd25q512.patch b/target/linux/generic/pending-6.6/482-mtd-spi-nor-add-gd25q512.patch
new file mode 100644 (file)
index 0000000..ddd3405
--- /dev/null
@@ -0,0 +1,23 @@
+From f8943df3beb0d3f9754bb35320c3a378727175a8 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Thu, 14 Jul 2022 08:38:07 +0200
+Subject: [PATCH] spi-nor/gigadevic: add gd25q512
+
+---
+ drivers/mtd/spi-nor/gigadevice.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/mtd/spi-nor/gigadevice.c
++++ b/drivers/mtd/spi-nor/gigadevice.c
+@@ -71,6 +71,11 @@ static const struct flash_info gigadevic
+               FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6)
+               FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
+               .fixups = &gd25q256_fixups },
++      { "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024)
++              FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++              FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
++              NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
++                            SPI_NOR_QUAD_READ) },
+ };
+ const struct spi_nor_manufacturer spi_nor_gigadevice = {
diff --git a/target/linux/generic/pending-6.6/484-mtd-spi-nor-add-esmt-f25l16pa.patch b/target/linux/generic/pending-6.6/484-mtd-spi-nor-add-esmt-f25l16pa.patch
new file mode 100644 (file)
index 0000000..d5ebe20
--- /dev/null
@@ -0,0 +1,24 @@
+From 87363cc0e522de3294ea6ae10fb468d2a8d6fb2f Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 12:17:21 +0200
+Subject: [PATCH] spi-nor/esmt.c: add esmt f25l16pa
+
+This fixes support for Dongwon T&I DW02-412H which uses F25L16PA(2S)
+flash.
+
+---
+ drivers/mtd/spi-nor/esmt.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/mtd/spi-nor/esmt.c
++++ b/drivers/mtd/spi-nor/esmt.c
+@@ -10,6 +10,9 @@
+ static const struct flash_info esmt_nor_parts[] = {
+       /* ESMT */
++      { "f25l16pa-2s", INFO(0x8c2115, 0, 64 * 1024, 32)
++                      FLAGS(SPI_NOR_HAS_LOCK)
++                      NO_SFDP_FLAGS(SECT_4K) },
+       { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64)
+               FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
+               NO_SFDP_FLAGS(SECT_4K) },
diff --git a/target/linux/generic/pending-6.6/485-mtd-spi-nor-add-xmc-xm25qh128c.patch b/target/linux/generic/pending-6.6/485-mtd-spi-nor-add-xmc-xm25qh128c.patch
new file mode 100644 (file)
index 0000000..e8583cc
--- /dev/null
@@ -0,0 +1,25 @@
+From f6b33d850f7f12555df2fa0e3349b33427bf5890 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 12:19:01 +0200
+Subject: [PATCH] spi-nor/xmc.c: add xm25qh128c
+
+The XMC XM25QH128C is a 16MB SPI NOR chip. The patch is verified on
+Ruijie RG-EW3200GX PRO.
+Datasheet available at https://www.xmcwh.com/uploads/435/XM25QH128C.pdf
+
+---
+ drivers/mtd/spi-nor/xmc.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/mtd/spi-nor/xmc.c
++++ b/drivers/mtd/spi-nor/xmc.c
+@@ -16,6 +16,9 @@ static const struct flash_info xmc_nor_p
+       { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256)
+               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+                             SPI_NOR_QUAD_READ) },
++      { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256)
++              NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
++                            SPI_NOR_QUAD_READ) },
+ };
+ const struct spi_nor_manufacturer spi_nor_xmc = {
diff --git a/target/linux/generic/pending-6.6/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch b/target/linux/generic/pending-6.6/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch
new file mode 100644 (file)
index 0000000..7a3a0f3
--- /dev/null
@@ -0,0 +1,170 @@
+From f32085fc0b87049491b07e198d924d738a1a2834 Mon Sep 17 00:00:00 2001
+From: Daniel Danzberger <daniel@dd-wrt.com>
+Date: Wed, 3 Aug 2022 17:31:03 +0200
+Subject: [PATCH] mtd: spinand: Add support for Etron EM73D044VCx
+
+Airoha is a new ARM platform based on Cortex-A53 which has recently been
+merged into linux-next.
+
+Due to BootROM limitations on this platform, the Cortex-A53 can't run in
+Aarch64 mode and code must be compiled for 32-Bit ARM.
+
+This support is based mostly on those linux-next commits backported
+for kernel 5.15.
+
+Patches:
+1 - platform support = linux-next
+2 - clock driver = linux-next
+3 - gpio driver = linux-next
+4 - linux,usable-memory-range dts support = linux-next
+5 - mtd spinand driver
+6 - spi driver
+7 - pci driver (kconfig only, uses mediatek PCI) = linux-next
+
+Still missing:
+- Ethernet driver
+- Sysupgrade support
+
+A.t.m there exists one subtarget EN7523 with only one evaluation
+board.
+
+The initramfs can be run with the following commands from u-boot:
+-
+u-boot> setenv bootfile \
+       openwrt-airoha-airoha_en7523-evb-initramfs-kernel.bin
+u-boot> tftpboot
+u-boot> bootm 0x81800000
+-
+
+Submitted-by: Daniel Danzberger <daniel@dd-wrt.com>
+
+--- a/drivers/mtd/nand/spi/Makefile
++++ b/drivers/mtd/nand/spi/Makefile
+@@ -1,4 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0
+-spinand-objs := core.o alliancememory.o ato.o esmt.o foresee.o gigadevice.o macronix.o
+-spinand-objs += micron.o paragon.o toshiba.o winbond.o xtx.o
++spinand-objs := core.o alliancememory.o ato.o esmt.o etron.o foresee.o gigadevice.o
++spinand-objs += 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
+@@ -940,6 +940,7 @@ static const struct spinand_manufacturer
+       &alliancememory_spinand_manufacturer,
+       &ato_spinand_manufacturer,
+       &esmt_c8_spinand_manufacturer,
++      &etron_spinand_manufacturer,
+       &foresee_spinand_manufacturer,
+       &gigadevice_spinand_manufacturer,
+       &macronix_spinand_manufacturer,
+--- /dev/null
++++ b/drivers/mtd/nand/spi/etron.c
+@@ -0,0 +1,98 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <linux/device.h>
++#include <linux/kernel.h>
++#include <linux/mtd/spinand.h>
++
++#define SPINAND_MFR_ETRON                     0xd5
++
++
++static SPINAND_OP_VARIANTS(read_cache_variants,
++              SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
++
++static SPINAND_OP_VARIANTS(write_cache_variants,
++              SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
++              SPINAND_PROG_LOAD(true, 0, NULL, 0));
++
++static SPINAND_OP_VARIANTS(update_cache_variants,
++              SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
++              SPINAND_PROG_LOAD(false, 0, NULL, 0));
++
++static int etron_ooblayout_ecc(struct mtd_info *mtd, int section,
++                                      struct mtd_oob_region *oobregion)
++{
++      if (section)
++              return -ERANGE;
++
++      oobregion->offset = 72;
++      oobregion->length = 56;
++
++      return 0;
++}
++
++static int etron_ooblayout_free(struct mtd_info *mtd, int section,
++                         struct mtd_oob_region *oobregion)
++{
++      if (section)
++              return -ERANGE;
++
++      oobregion->offset = 1;
++      oobregion->length = 71;
++
++      return 0;
++}
++
++static int etron_ecc_get_status(struct spinand_device *spinand, u8 status)
++{
++      switch (status & STATUS_ECC_MASK) {
++      case STATUS_ECC_NO_BITFLIPS:
++              return 0;
++
++      case STATUS_ECC_HAS_BITFLIPS:
++              /* Between 1-7 bitflips were corrected */
++              return 7;
++
++      case STATUS_ECC_MASK:
++              /* Maximum bitflips were corrected */
++              return 8;
++
++      case STATUS_ECC_UNCOR_ERROR:
++              return -EBADMSG;
++      }
++
++      return -EINVAL;
++}
++
++static const struct mtd_ooblayout_ops etron_ooblayout = {
++      .ecc = etron_ooblayout_ecc,
++      .free = etron_ooblayout_free,
++};
++
++static const struct spinand_info etron_spinand_table[] = {
++      SPINAND_INFO("EM73D044VCx",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x1f),
++                   // bpc, pagesize, oobsize, pagesperblock, bperlun, maxbadplun, ppl, lpt, #t
++                   NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
++};
++
++static const struct spinand_manufacturer_ops etron_spinand_manuf_ops = {
++};
++
++const struct spinand_manufacturer etron_spinand_manufacturer = {
++      .id = SPINAND_MFR_ETRON,
++      .name = "Etron",
++      .chips = etron_spinand_table,
++      .nchips = ARRAY_SIZE(etron_spinand_table),
++      .ops = &etron_spinand_manuf_ops,
++};
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -263,6 +263,7 @@ struct spinand_manufacturer {
+ extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
+ extern const struct spinand_manufacturer ato_spinand_manufacturer;
+ extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
++extern const struct spinand_manufacturer etron_spinand_manufacturer;
+ extern const struct spinand_manufacturer foresee_spinand_manufacturer;
+ extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
+ extern const struct spinand_manufacturer macronix_spinand_manufacturer;
diff --git a/target/linux/generic/pending-6.6/488-mtd-spi-nor-add-xmc-xm25qh64c.patch b/target/linux/generic/pending-6.6/488-mtd-spi-nor-add-xmc-xm25qh64c.patch
new file mode 100644 (file)
index 0000000..e1e4f25
--- /dev/null
@@ -0,0 +1,23 @@
+From: Joe Mullally <jwmullally@gmail.com>
+Subject: mtd/spi-nor/xmc: add support for XMC XM25QH64C
+
+The XMC XM25QH64C is a 8MB SPI NOR chip. The patch is verified on TL-WPA8631P v3.
+Datasheet available at https://www.xmcwh.com/uploads/442/XM25QH64C.pdf
+
+Signed-off-by: Joe Mullally <jwmullally@gmail.com>
+---
+ drivers/mtd/spi-nor/xmc.c                             | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/mtd/spi-nor/xmc.c
++++ b/drivers/mtd/spi-nor/xmc.c
+@@ -13,6 +13,9 @@ static const struct flash_info xmc_nor_p
+       { "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128)
+               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+                             SPI_NOR_QUAD_READ) },
++      { "XM25QH64C", INFO(0x204017, 0, 64 * 1024, 128)
++              NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
++                            SPI_NOR_QUAD_READ) },
+       { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256)
+               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+                             SPI_NOR_QUAD_READ) },
diff --git a/target/linux/generic/pending-6.6/489-mtd-spinand-winbond-add-support-for-W25N01KV.patch b/target/linux/generic/pending-6.6/489-mtd-spinand-winbond-add-support-for-W25N01KV.patch
new file mode 100644 (file)
index 0000000..b72e28a
--- /dev/null
@@ -0,0 +1,63 @@
+From 446daf20b0a6790751459cdde0ff9fc8813e54d1 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Mon, 29 Jul 2024 14:09:16 +0200
+Subject: [PATCH] mtd: spinand: winbond: add support for W25N01KV
+
+Add support for Winbond W25N01KV 1Gbit SPI-NAND.
+
+It has 4-bit on-die ECC.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+---
+ drivers/mtd/nand/spi/winbond.c | 26 ++++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+--- a/drivers/mtd/nand/spi/winbond.c
++++ b/drivers/mtd/nand/spi/winbond.c
+@@ -76,6 +76,18 @@ static int w25m02gv_select_target(struct
+       return spi_mem_exec_op(spinand->spimem, &op);
+ }
++static int w25n01kv_ooblayout_ecc(struct mtd_info *mtd, int section,
++                                struct mtd_oob_region *region)
++{
++      if (section > 3)
++              return -ERANGE;
++
++      region->offset = 64 + (8 * section);
++      region->length = 7;
++
++      return 0;
++}
++
+ static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *region)
+ {
+@@ -100,6 +112,11 @@ static int w25n02kv_ooblayout_free(struc
+       return 0;
+ }
++static const struct mtd_ooblayout_ops w25n01kv_ooblayout = {
++      .ecc = w25n01kv_ooblayout_ecc,
++      .free = w25n02kv_ooblayout_free,
++};
++
+ static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
+       .ecc = w25n02kv_ooblayout_ecc,
+       .free = w25n02kv_ooblayout_free,
+@@ -163,6 +180,15 @@ static const struct spinand_info winbond
+                                             &update_cache_variants),
+                    0,
+                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
++      SPINAND_INFO("W25N01KV",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21),
++                   NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25n01kv_ooblayout, w25n02kv_ecc_get_status)),
+       SPINAND_INFO("W25N02KV",
+                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
diff --git a/target/linux/generic/pending-6.6/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch b/target/linux/generic/pending-6.6/490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch
new file mode 100644 (file)
index 0000000..defde3f
--- /dev/null
@@ -0,0 +1,104 @@
+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
+@@ -1258,6 +1258,80 @@ static struct mtd_notifier ubi_mtd_notif
+       .remove = ubi_notify_remove,
+ };
++
++/*
++ * 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;
++      struct device_node *np;
++      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;
++
++      /* skip "linux,ubi" mtd as it has already been attached */
++      np = mtd_get_of_node(mtd);
++      if (of_device_is_compatible(np, "linux,ubi"))
++              goto cleanup;
++
++      /* 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, false);
++      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_attach(void)
+ {
+       int err, i, k;
+@@ -1308,6 +1382,12 @@ static int __init ubi_init_attach(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();
++
+       return 0;
+ out_detach:
diff --git a/target/linux/generic/pending-6.6/491-ubi-auto-create-ubiblock-device-for-rootfs.patch b/target/linux/generic/pending-6.6/491-ubi-auto-create-ubiblock-device-for-rootfs.patch
new file mode 100644 (file)
index 0000000..0b607fa
--- /dev/null
@@ -0,0 +1,77 @@
+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
+@@ -570,10 +570,47 @@ match_volume_desc(struct ubi_volume_info
+       return true;
+ }
++#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 ubiblock_create_auto_rootfs(struct ubi_volume_info *vi)
++{
++      int ret, is_ubifs;
++      struct ubi_volume_desc *desc;
++
++      if (strcmp(vi->name, "rootfs") &&
++          strcmp(vi->name, "fit"))
++              return;
++
++      desc = ubi_open_volume(vi->ubi_num, vi->vol_id, UBI_READONLY);
++      if (IS_ERR(desc))
++              return;
++
++      is_ubifs = ubi_vol_is_ubifs(desc);
++      ubi_close_volume(desc);
++      if (is_ubifs)
++              return;
++
++      ret = ubiblock_create(vi);
++      if (ret)
++              pr_err("UBI error: block: can't add '%s' volume, err=%d\n",
++                      vi->name, ret);
++}
++
+ static void
+ ubiblock_create_from_param(struct ubi_volume_info *vi)
+ {
+       int i, ret = 0;
++      bool got_param = false;
+       struct ubiblock_param *p;
+       /*
+@@ -586,6 +623,7 @@ ubiblock_create_from_param(struct ubi_vo
+               if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id))
+                       continue;
++              got_param = true;
+               ret = ubiblock_create(vi);
+               if (ret) {
+                       pr_err(
+@@ -594,6 +632,10 @@ ubiblock_create_from_param(struct ubi_vo
+               }
+               break;
+       }
++
++      /* auto-attach "rootfs" volume if existing and non-ubifs */
++      if (!got_param && IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV))
++              ubiblock_create_auto_rootfs(vi);
+ }
+ static int ubiblock_notify(struct notifier_block *nb,
diff --git a/target/linux/generic/pending-6.6/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch b/target/linux/generic/pending-6.6/492-try-auto-mounting-ubi0-rootfs-in-init-do_mounts.c.patch
new file mode 100644 (file)
index 0000000..297789e
--- /dev/null
@@ -0,0 +1,54 @@
+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
+@@ -248,7 +248,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
+@@ -385,6 +408,11 @@ static inline void mount_block_root(char
+ void __init mount_root(char *root_device_name)
+ {
++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
++      if (!mount_ubi_rootfs())
++              return;
++#endif
++
+       switch (ROOT_DEV) {
+       case Root_NFS:
+               mount_nfs_root();
diff --git a/target/linux/generic/pending-6.6/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch b/target/linux/generic/pending-6.6/493-ubi-set-ROOT_DEV-to-ubiblock-rootfs-if-unset.patch
new file mode 100644 (file)
index 0000000..367bf65
--- /dev/null
@@ -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
+@@ -41,6 +41,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"
+@@ -428,6 +429,15 @@ int ubiblock_create(struct ubi_volume_in
+       dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)",
+                dev->ubi_num, dev->vol_id, vi->name);
+       mutex_unlock(&devices_mutex);
++
++      if (!strcmp(vi->name, "rootfs") &&
++          IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
++          ROOT_DEV == 0) {
++              pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n",
++                        dev->ubi_num, dev->vol_id, vi->name);
++              ROOT_DEV = MKDEV(gd->major, gd->first_minor);
++      }
++
+       return 0;
+ out_remove_minor:
diff --git a/target/linux/generic/pending-6.6/494-mtd-ubi-add-EOF-marker-support.patch b/target/linux/generic/pending-6.6/494-mtd-ubi-add-EOF-marker-support.patch
new file mode 100644 (file)
index 0000000..fc48146
--- /dev/null
@@ -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
+@@ -780,6 +780,7 @@ struct ubi_attach_info {
+       int mean_ec;
+       uint64_t ec_sum;
+       int ec_count;
++      bool eof_found;
+       struct kmem_cache *aeb_slab_cache;
+       struct ubi_ec_hdr *ech;
+       struct ubi_vid_io_buf *vidb;
diff --git a/target/linux/generic/pending-6.6/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/target/linux/generic/pending-6.6/496-dt-bindings-add-bindings-for-mtd-concat-devices.patch
new file mode 100644 (file)
index 0000000..01f3b9e
--- /dev/null
@@ -0,0 +1,52 @@
+From 5734c6669fba7ddb5ef491ccff7159d15dba0b59 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Wed, 5 Sep 2018 01:32:51 +0200
+Subject: [PATCH 496/497] dt-bindings: add bindings for mtd-concat devices
+
+Document virtual mtd-concat device bindings.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+---
+ .../devicetree/bindings/mtd/mtd-concat.txt    | 36 +++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/mtd-concat.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/mtd-concat.txt
+@@ -0,0 +1,36 @@
++Virtual MTD concat device
++
++Requires properties:
++- devices: list of phandles to mtd nodes that should be concatenated
++
++Example:
++
++&spi {
++      flash0: flash@0 {
++              ...
++      };
++      flash1: flash@1 {
++              ...
++      };
++};
++
++flash {
++      compatible = "mtd-concat";
++
++      devices = <&flash0 &flash1>;
++
++      partitions {
++              compatible = "fixed-partitions";
++
++              partition@0 {
++                      label = "boot";
++                      reg = <0x0000000 0x0040000>;
++                      read-only;
++              };
++
++              partition@40000 {
++                      label = "firmware";
++                      reg = <0x0040000 0x1fc0000>;
++              };
++      }
++}
diff --git a/target/linux/generic/pending-6.6/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/target/linux/generic/pending-6.6/497-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch
new file mode 100644 (file)
index 0000000..e0cbc45
--- /dev/null
@@ -0,0 +1,216 @@
+From e53f712d8eac71f54399b61038ccf87d2cee99d7 Mon Sep 17 00:00:00 2001
+From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+Date: Sat, 25 Aug 2018 12:35:22 +0200
+Subject: [PATCH 497/497] mtd: mtdconcat: add dt driver for concat devices
+
+Some mtd drivers like physmap variants have support for concatenating
+multiple mtd devices, but there is no generic way to define such a
+concat device from within the device tree.
+
+This is useful for some SoC boards that use multiple flash chips as
+memory banks of a single mtd device, with partitions spanning chip
+borders.
+
+This commit adds a driver for creating virtual mtd-concat devices. They
+must have a compatible = "mtd-concat" line, and define a list of devices
+to concat in the 'devices' property, for example:
+
+flash {
+  compatible = "mtd-concat";
+
+  devices = <&flash0 &flash1>;
+
+  partitions {
+    ...
+  };
+};
+
+The driver is added to the very end of the mtd Makefile to increase the
+likelyhood of all child devices already being loaded at the time of
+probing, preventing unnecessary deferred probes.
+
+Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
+---
+ drivers/mtd/Kconfig                 |   2 +
+ drivers/mtd/Makefile                |   3 +
+ drivers/mtd/composite/Kconfig       |  12 +++
+ drivers/mtd/composite/Makefile      |   6 ++
+ drivers/mtd/composite/virt_concat.c | 128 ++++++++++++++++++++++++++++
+ 5 files changed, 151 insertions(+)
+ create mode 100644 drivers/mtd/composite/Kconfig
+ create mode 100644 drivers/mtd/composite/Makefile
+ create mode 100644 drivers/mtd/composite/virt_concat.c
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -241,4 +241,6 @@ source "drivers/mtd/ubi/Kconfig"
+ source "drivers/mtd/hyperbus/Kconfig"
++source "drivers/mtd/composite/Kconfig"
++
+ endif # MTD
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -33,3 +33,6 @@ obj-y                += chips/ lpddr/ maps/ devices/ n
+ obj-$(CONFIG_MTD_SPI_NOR)     += spi-nor/
+ obj-$(CONFIG_MTD_UBI)         += ubi/
+ obj-$(CONFIG_MTD_HYPERBUS)    += hyperbus/
++
++# Composite drivers must be loaded last
++obj-y                         += composite/
+--- /dev/null
++++ b/drivers/mtd/composite/Kconfig
+@@ -0,0 +1,12 @@
++menu "Composite MTD device drivers"
++      depends on MTD!=n
++
++config MTD_VIRT_CONCAT
++      tristate "Virtual concat MTD device"
++      help
++        This driver allows creation of a virtual MTD concat device, which
++        concatenates multiple underlying MTD devices to a single device.
++        This is required by some SoC boards where multiple memory banks are
++        used as one device with partitions spanning across device boundaries.
++
++endmenu
+--- /dev/null
++++ b/drivers/mtd/composite/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# linux/drivers/mtd/composite/Makefile
++#
++
++obj-$(CONFIG_MTD_VIRT_CONCAT)   += virt_concat.o
+--- /dev/null
++++ b/drivers/mtd/composite/virt_concat.c
+@@ -0,0 +1,128 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Virtual concat MTD device driver
++ *
++ * Copyright (C) 2018 Bernhard Frauendienst
++ * Author: Bernhard Frauendienst, kernel@nospam.obeliks.de
++ */
++
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/mtd/concat.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/slab.h>
++
++/*
++ * struct of_virt_concat - platform device driver data.
++ * @cmtd the final mtd_concat device
++ * @num_devices the number of devices in @devices
++ * @devices points to an array of devices already loaded
++ */
++struct of_virt_concat {
++      struct mtd_info *cmtd;
++      int num_devices;
++      struct mtd_info **devices;
++};
++
++static int virt_concat_remove(struct platform_device *pdev)
++{
++      struct of_virt_concat *info;
++      int i;
++
++      info = platform_get_drvdata(pdev);
++      if (!info)
++              return 0;
++
++      // unset data for when this is called after a probe error
++      platform_set_drvdata(pdev, NULL);
++
++      if (info->cmtd) {
++              mtd_device_unregister(info->cmtd);
++              mtd_concat_destroy(info->cmtd);
++      }
++
++      if (info->devices) {
++              for (i = 0; i < info->num_devices; i++)
++                      put_mtd_device(info->devices[i]);
++      }
++
++      return 0;
++}
++
++static int virt_concat_probe(struct platform_device *pdev)
++{
++      struct device_node *node = pdev->dev.of_node;
++      struct of_phandle_iterator it;
++      struct of_virt_concat *info;
++      struct mtd_info *mtd;
++      int err = 0, count;
++
++      count = of_count_phandle_with_args(node, "devices", NULL);
++      if (count <= 0)
++              return -EINVAL;
++
++      info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
++      if (!info)
++              return -ENOMEM;
++      info->devices = devm_kcalloc(&pdev->dev, count,
++                                   sizeof(*(info->devices)), GFP_KERNEL);
++      if (!info->devices) {
++              err = -ENOMEM;
++              goto err_remove;
++      }
++
++      platform_set_drvdata(pdev, info);
++
++      of_for_each_phandle(&it, err, node, "devices", NULL, 0) {
++              mtd = of_get_mtd_device_by_node(it.node);
++              if (IS_ERR(mtd)) {
++                      of_node_put(it.node);
++                      err = -EPROBE_DEFER;
++                      goto err_remove;
++              }
++
++              info->devices[info->num_devices++] = mtd;
++      }
++
++      info->cmtd = mtd_concat_create(info->devices, info->num_devices,
++                                     dev_name(&pdev->dev));
++      if (!info->cmtd) {
++              err = -ENXIO;
++              goto err_remove;
++      }
++
++      info->cmtd->dev.parent = &pdev->dev;
++      mtd_set_of_node(info->cmtd, node);
++      mtd_device_register(info->cmtd, NULL, 0);
++
++      return 0;
++
++err_remove:
++      virt_concat_remove(pdev);
++
++      return err;
++}
++
++static const struct of_device_id virt_concat_of_match[] = {
++      { .compatible = "mtd-concat", },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, virt_concat_of_match);
++
++static struct platform_driver virt_concat_driver = {
++      .probe = virt_concat_probe,
++      .remove = virt_concat_remove,
++      .driver  = {
++              .name   = "virt-mtdconcat",
++              .of_match_table = virt_concat_of_match,
++      },
++};
++
++module_platform_driver(virt_concat_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Bernhard Frauendienst <kernel@nospam.obeliks.de>");
++MODULE_DESCRIPTION("Virtual concat MTD device driver");
diff --git a/target/linux/generic/pending-6.6/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch b/target/linux/generic/pending-6.6/498-mtd-spi-nor-locking-support-for-MX25L6405D.patch
new file mode 100644 (file)
index 0000000..1a4d5a7
--- /dev/null
@@ -0,0 +1,32 @@
+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
+@@ -48,6 +48,7 @@ static const struct flash_info macronix_
+       { "mx25l3255e",  INFO(0xc29e16, 0, 64 * 1024,  64)
+               NO_SFDP_FLAGS(SECT_4K) },
+       { "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128)
++              FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP)
+               NO_SFDP_FLAGS(SECT_4K) },
+       { "mx25u2033e",  INFO(0xc22532, 0, 64 * 1024,   4)
+               NO_SFDP_FLAGS(SECT_4K) },
diff --git a/target/linux/generic/pending-6.6/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch b/target/linux/generic/pending-6.6/499-mtd-spi-nor-disable-16-bit-sr-for-macronix.patch
new file mode 100644 (file)
index 0000000..ea580a9
--- /dev/null
@@ -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
+@@ -115,6 +115,7 @@ static int macronix_nor_late_init(struct
+ {
+       if (!nor->params->set_4byte_addr_mode)
+               nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b;
++      nor->flags &= ~SNOR_F_HAS_16BIT_SR;
+       nor->flags |= SNOR_F_HAS_LOCK;
+       return 0;
diff --git a/target/linux/generic/pending-6.6/500-fs_cdrom_dependencies.patch b/target/linux/generic/pending-6.6/500-fs_cdrom_dependencies.patch
new file mode 100644 (file)
index 0000000..7c14358
--- /dev/null
@@ -0,0 +1,52 @@
+From af7b91bcecce0eae24e90acd35d96ecee73e1407 Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 12:21:15 +0200
+Subject: [PATCH] fs: add cdrom dependency
+
+---
+ fs/hfs/Kconfig     | 1 +
+ fs/hfsplus/Kconfig | 1 +
+ fs/isofs/Kconfig   | 1 +
+ fs/udf/Kconfig     | 1 +
+ 4 files changed, 4 insertions(+)
+
+--- a/fs/hfs/Kconfig
++++ b/fs/hfs/Kconfig
+@@ -2,6 +2,7 @@
+ config HFS_FS
+       tristate "Apple Macintosh file system support"
+       depends on BLOCK
++      select CDROM
+       select BUFFER_HEAD
+       select NLS
+       select LEGACY_DIRECT_IO
+--- 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 BUFFER_HEAD
+       select NLS
+       select NLS_UTF8
+--- 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
+       select BUFFER_HEAD
+       help
+         This is the standard file system used on CD-ROMs.  It was previously
+--- 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 BUFFER_HEAD
+       select CRC_ITU_T
+       select NLS
diff --git a/target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch b/target/linux/generic/pending-6.6/510-block-add-uImage.FIT-subimage-block-driver.patch
new file mode 100644 (file)
index 0000000..b06c29f
--- /dev/null
@@ -0,0 +1,755 @@
+From 6173a065cb395d4a9528c4e49810af127db68141 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 16 Nov 2022 12:49:52 +0000
+Subject: [PATCH 1/2] block: add uImage.FIT subimage block driver
+
+Add a small block driver which exposes filesystem sub-images contained
+in U-Boot uImage.FIT images as block devices.
+
+The uImage.FIT image has to be stored directly on a block device or
+partition, MTD device or partition, or UBI volume.
+
+The driver is intended for systems using the U-Boot bootloader and
+uses the root device hint left by the bootloader (or the user) in
+the 'chosen' section of the device-tree.
+
+Example:
+/dts-v1/;
+/ {
+        chosen {
+                rootdisk = <&mmc0_part3>;
+        };
+};
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ MAINTAINERS                 |   6 +
+ drivers/block/Kconfig       |  12 +
+ drivers/block/Makefile      |   2 +
+ drivers/block/fitblk.c      | 658 ++++++++++++++++++++++++++++++++++++
+ drivers/block/open          |   4 +
+ include/uapi/linux/fitblk.h |  10 +
+ 6 files changed, 692 insertions(+)
+ create mode 100644 drivers/block/fitblk.c
+ create mode 100644 drivers/block/open
+ create mode 100644 include/uapi/linux/fitblk.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -22015,6 +22015,12 @@ F:    Documentation/filesystems/ubifs-authe
+ F:    Documentation/filesystems/ubifs.rst
+ F:    fs/ubifs/
++U-BOOT UIMAGE.FIT PARSER
++M:    Daniel Golle <daniel@makrotopia.org>
++L:    linux-block@vger.kernel.org
++S:    Maintained
++F:    drivers/block/fitblk.c
++
+ UBLK USERSPACE BLOCK DRIVER
+ M:    Ming Lei <ming.lei@redhat.com>
+ L:    linux-block@vger.kernel.org
+--- a/drivers/block/Kconfig
++++ b/drivers/block/Kconfig
+@@ -354,6 +354,18 @@ config VIRTIO_BLK
+         This is the virtual block driver for virtio.  It can be used with
+           QEMU based VMMs (like KVM or Xen).  Say Y or M.
++config UIMAGE_FIT_BLK
++      bool "uImage.FIT block driver"
++      help
++        This driver allows using filesystems contained in uImage.FIT images
++        by mapping them as block devices.
++
++        It can currently not be built as a module due to libfdt symbols not
++        being exported.
++
++        Say Y if you want to mount filesystems sub-images of a uImage.FIT
++        stored in a block device partition, mtdblock or ubiblock device.
++
+ config BLK_DEV_RBD
+       tristate "Rados block device (RBD)"
+       depends on INET && BLOCK
+--- a/drivers/block/Makefile
++++ b/drivers/block/Makefile
+@@ -39,4 +39,6 @@ obj-$(CONFIG_BLK_DEV_NULL_BLK)       += null_b
+ obj-$(CONFIG_BLK_DEV_UBLK)                    += ublk_drv.o
++obj-$(CONFIG_UIMAGE_FIT_BLK)  += fitblk.o
++
+ swim_mod-y    := swim.o swim_asm.o
+--- /dev/null
++++ b/drivers/block/fitblk.c
+@@ -0,0 +1,658 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * uImage.FIT virtual block device driver.
++ *
++ * Copyright (C) 2023 Daniel Golle
++ * Copyright (C) 2007 Nick Piggin
++ * Copyright (C) 2007 Novell Inc.
++ *
++ * Initially derived from drivers/block/brd.c which is in parts derived from
++ * drivers/block/rd.c, and drivers/block/loop.c, copyright of their respective
++ * owners.
++ *
++ * uImage.FIT headers extracted from Das U-Boot
++ *  (C) Copyright 2008 Semihalf
++ *  (C) Copyright 2000-2005
++ *  Wolfgang Denk, DENX Software Engineering, wd@denx.de.
++ */
++
++#include <linux/init.h>
++#include <linux/initrd.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/major.h>
++#include <linux/blkdev.h>
++#include <linux/blkpg.h>
++#include <linux/blk-mq.h>
++#include <linux/ctype.h>
++#include <linux/hdreg.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_fdt.h>
++#include <linux/pagemap.h>
++#include <linux/platform_device.h>
++#include <linux/property.h>
++#include <linux/refcount.h>
++#include <linux/task_work.h>
++#include <linux/types.h>
++#include <linux/libfdt.h>
++#include <linux/mtd/mtd.h>
++#include <linux/root_dev.h>
++#include <uapi/linux/fitblk.h>
++
++#define FIT_DEVICE_PREFIX     "fit"
++
++/* maximum number of pages used for the uImage.FIT index structure */
++#define FIT_MAX_PAGES         1024
++
++/* minimum free sectors to map as read-write "remainder" volume */
++#define MIN_FREE_SECT         16
++
++/* maximum number of mapped loadables */
++#define MAX_FIT_LOADABLES     16
++
++/* constants for uImage.FIT structrure traversal */
++#define FIT_IMAGES_PATH               "/images"
++#define FIT_CONFS_PATH                "/configurations"
++
++/* hash/signature/key node */
++#define FIT_HASH_NODENAME     "hash"
++#define FIT_ALGO_PROP         "algo"
++#define FIT_VALUE_PROP                "value"
++#define FIT_IGNORE_PROP               "uboot-ignore"
++#define FIT_SIG_NODENAME      "signature"
++#define FIT_KEY_REQUIRED      "required"
++#define FIT_KEY_HINT          "key-name-hint"
++
++/* cipher node */
++#define FIT_CIPHER_NODENAME   "cipher"
++#define FIT_ALGO_PROP         "algo"
++
++/* image node */
++#define FIT_DATA_PROP         "data"
++#define FIT_DATA_POSITION_PROP        "data-position"
++#define FIT_DATA_OFFSET_PROP  "data-offset"
++#define FIT_DATA_SIZE_PROP    "data-size"
++#define FIT_TIMESTAMP_PROP    "timestamp"
++#define FIT_DESC_PROP         "description"
++#define FIT_ARCH_PROP         "arch"
++#define FIT_TYPE_PROP         "type"
++#define FIT_OS_PROP           "os"
++#define FIT_COMP_PROP         "compression"
++#define FIT_ENTRY_PROP                "entry"
++#define FIT_LOAD_PROP         "load"
++
++/* configuration node */
++#define FIT_KERNEL_PROP               "kernel"
++#define FIT_FILESYSTEM_PROP   "filesystem"
++#define FIT_RAMDISK_PROP      "ramdisk"
++#define FIT_FDT_PROP          "fdt"
++#define FIT_LOADABLE_PROP     "loadables"
++#define FIT_DEFAULT_PROP      "default"
++#define FIT_SETUP_PROP                "setup"
++#define FIT_FPGA_PROP         "fpga"
++#define FIT_FIRMWARE_PROP     "firmware"
++#define FIT_STANDALONE_PROP   "standalone"
++
++/* fitblk driver data */
++static const char *_fitblk_claim_ptr = "I belong to fitblk";
++static const char *ubootver;
++struct device_node *rootdisk;
++static struct platform_device *pdev;
++static LIST_HEAD(fitblk_devices);
++static DEFINE_MUTEX(devices_mutex);
++refcount_t num_devs;
++
++struct fitblk {
++      struct platform_device  *pdev;
++      struct block_device     *lower_bdev;
++      sector_t                start_sect;
++      struct gendisk          *disk;
++      struct work_struct      remove_work;
++      struct list_head        list;
++      bool                    dead;
++};
++
++static int fitblk_open(struct gendisk *disk, fmode_t mode)
++{
++      struct fitblk *fitblk = disk->private_data;
++
++      if (fitblk->dead)
++              return -ENOENT;
++
++      return 0;
++}
++
++static void fitblk_release(struct gendisk *disk)
++{
++      return;
++}
++
++static void fitblk_submit_bio(struct bio *orig_bio)
++{
++      struct bio *bio = orig_bio;
++      struct fitblk *fitblk = bio->bi_bdev->bd_disk->private_data;
++
++      if (fitblk->dead)
++              return;
++
++      /* mangle bio and re-submit */
++      while (bio) {
++              bio->bi_iter.bi_sector += fitblk->start_sect;
++              bio->bi_bdev = fitblk->lower_bdev;
++              bio = bio->bi_next;
++      }
++      submit_bio(orig_bio);
++}
++
++static void fitblk_remove(struct fitblk *fitblk)
++{
++      blk_mark_disk_dead(fitblk->disk);
++      mutex_lock(&devices_mutex);
++      fitblk->dead = true;
++      list_del(&fitblk->list);
++      mutex_unlock(&devices_mutex);
++
++      schedule_work(&fitblk->remove_work);
++}
++
++static int fitblk_ioctl(struct block_device *bdev, fmode_t mode,
++                      unsigned int cmd, unsigned long arg)
++{
++      struct fitblk *fitblk = bdev->bd_disk->private_data;
++
++      if (!capable(CAP_SYS_ADMIN))
++              return -EACCES;
++
++      if (fitblk->dead)
++              return -ENOENT;
++
++      switch (cmd) {
++      case FITBLK_RELEASE:
++              fitblk_remove(fitblk);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static const struct block_device_operations fitblk_fops = {
++      .owner          = THIS_MODULE,
++      .ioctl          = fitblk_ioctl,
++      .open           = fitblk_open,
++      .release        = fitblk_release,
++      .submit_bio     = fitblk_submit_bio,
++};
++
++static void fitblk_purge(struct work_struct *work)
++{
++      struct fitblk *fitblk = container_of(work, struct fitblk, remove_work);
++
++      //del_gendisk(fitblk->disk); // causes crash, not doing it doesn't matter
++      refcount_dec(&num_devs);
++      platform_device_del(fitblk->pdev);
++      platform_device_put(fitblk->pdev);
++
++      if (refcount_dec_if_one(&num_devs)) {
++              sysfs_remove_link(&pdev->dev.kobj, "lower_dev");
++              blkdev_put(fitblk->lower_bdev, &_fitblk_claim_ptr);
++      }
++
++      kfree(fitblk);
++}
++
++static int add_fit_subimage_device(struct block_device *lower_bdev,
++                                 unsigned int slot, sector_t start_sect,
++                                 sector_t nr_sect, bool readonly)
++{
++      struct fitblk *fitblk;
++      struct gendisk *disk;
++      int err;
++
++      mutex_lock(&devices_mutex);
++      if (!refcount_inc_not_zero(&num_devs))
++              return -EBADF;
++
++      fitblk = kzalloc(sizeof(struct fitblk), GFP_KERNEL);
++      if (!fitblk) {
++              err = -ENOMEM;
++              goto out_unlock;
++      }
++
++      fitblk->lower_bdev = lower_bdev;
++      fitblk->start_sect = start_sect;
++      INIT_WORK(&fitblk->remove_work, fitblk_purge);
++
++      disk = blk_alloc_disk(NUMA_NO_NODE);
++      if (!disk) {
++              err = -ENOMEM;
++              goto out_free_fitblk;
++      }
++
++      disk->first_minor = 0;
++      disk->flags = lower_bdev->bd_disk->flags | GENHD_FL_NO_PART;
++      disk->fops = &fitblk_fops;
++      disk->private_data = fitblk;
++      if (readonly) {
++              set_disk_ro(disk, 1);
++              snprintf(disk->disk_name, sizeof(disk->disk_name), FIT_DEVICE_PREFIX "%u", slot);
++      } else {
++              strcpy(disk->disk_name, FIT_DEVICE_PREFIX "rw");
++      }
++
++      set_capacity(disk, nr_sect);
++
++      disk->queue->queue_flags = lower_bdev->bd_disk->queue->queue_flags;
++      memcpy(&disk->queue->limits, &lower_bdev->bd_disk->queue->limits,
++             sizeof(struct queue_limits));
++
++      fitblk->disk = disk;
++      fitblk->pdev = platform_device_alloc(disk->disk_name, PLATFORM_DEVID_NONE);
++      if (!fitblk->pdev) {
++              err = -ENOMEM;
++              goto out_cleanup_disk;
++      }
++
++      fitblk->pdev->dev.parent = &pdev->dev;
++      err = platform_device_add(fitblk->pdev);
++      if (err)
++              goto out_put_pdev;
++
++      err = device_add_disk(&fitblk->pdev->dev, disk, NULL);
++      if (err)
++              goto out_del_pdev;
++
++      if (!ROOT_DEV)
++              ROOT_DEV = disk->part0->bd_dev;
++
++      list_add_tail(&fitblk->list, &fitblk_devices);
++
++      mutex_unlock(&devices_mutex);
++
++      return 0;
++
++out_del_pdev:
++      platform_device_del(fitblk->pdev);
++out_put_pdev:
++      platform_device_put(fitblk->pdev);
++out_cleanup_disk:
++      put_disk(disk);
++out_free_fitblk:
++      kfree(fitblk);
++out_unlock:
++      refcount_dec(&num_devs);
++      mutex_unlock(&devices_mutex);
++      return err;
++}
++
++static void fitblk_mark_dead(struct block_device *bdev, bool surprise)
++{
++      struct list_head *n, *tmp;
++      struct fitblk *fitblk;
++
++      mutex_lock(&devices_mutex);
++      list_for_each_safe(n, tmp, &fitblk_devices) {
++              fitblk = list_entry(n, struct fitblk, list);
++              if (fitblk->lower_bdev != bdev)
++                      continue;
++
++              fitblk->dead = true;
++              list_del(&fitblk->list);
++              /* removal needs to be deferred to avoid deadlock */
++              schedule_work(&fitblk->remove_work);
++      }
++      mutex_unlock(&devices_mutex);
++}
++
++static const struct blk_holder_ops fitblk_hops = {
++      .mark_dead = fitblk_mark_dead,
++};
++
++static int parse_fit_on_dev(struct device *dev)
++{
++      struct block_device *bdev;
++      struct address_space *mapping;
++      struct folio *folio;
++      pgoff_t f_index = 0;
++      size_t bytes_left, bytes_to_copy;
++      void *pre_fit, *fit, *fit_c;
++      u64 dsize, dsectors, imgmaxsect = 0;
++      u32 size, image_pos, image_len;
++      const __be32 *image_offset_be, *image_len_be, *image_pos_be;
++      int ret = 0, node, images, config;
++      const char *image_name, *image_type, *image_description,
++              *config_default, *config_description, *config_loadables;
++      u32 image_name_len, image_type_len, image_description_len,
++              bootconf_len, config_default_len, config_description_len,
++              config_loadables_len;
++      sector_t start_sect, nr_sects;
++      struct device_node *np = NULL;
++      const char *bootconf_c;
++      const char *loadable;
++      char *bootconf = NULL, *bootconf_term;
++      bool found;
++      int loadables_rem_len, loadable_len;
++      u16 loadcnt;
++      unsigned int slot = 0;
++
++      /* Exclusive open the block device to receive holder notifications */
++      bdev = blkdev_get_by_dev(dev->devt, BLK_OPEN_READ, &_fitblk_claim_ptr, &fitblk_hops);
++      if (!bdev)
++              return -ENODEV;
++
++      if (IS_ERR(bdev))
++              return PTR_ERR(bdev);
++
++      mapping = bdev->bd_inode->i_mapping;
++
++      /* map first page */
++      folio = read_mapping_folio(mapping, f_index++, NULL);
++      if (IS_ERR(folio)) {
++              ret = PTR_ERR(folio);
++              goto out_blkdev;
++      }
++      pre_fit = folio_address(folio) + offset_in_folio(folio, 0);
++
++      /* uImage.FIT is based on flattened device tree structure */
++      if (fdt_check_header(pre_fit)) {
++              ret = -EINVAL;
++              folio_put(folio);
++              goto out_blkdev;
++      }
++
++      size = fdt_totalsize(pre_fit);
++
++      if (size > PAGE_SIZE * FIT_MAX_PAGES) {
++              ret = -EOPNOTSUPP;
++              folio_put(folio);
++              goto out_blkdev;
++      }
++
++      /* acquire disk size */
++      dsectors = bdev_nr_sectors(bdev);
++      dsize = dsectors << SECTOR_SHIFT;
++
++      /* abort if FIT structure is larger than disk or partition size */
++      if (size >= dsize) {
++              ret = -EFBIG;
++              folio_put(folio);
++              goto out_blkdev;
++      }
++
++      fit = kmalloc(size, GFP_KERNEL);
++      if (!fit) {
++              ret = -ENOMEM;
++              folio_put(folio);
++              goto out_blkdev;
++      }
++
++      bytes_left = size;
++      fit_c = fit;
++      while (bytes_left > 0) {
++              bytes_to_copy = min_t(size_t, bytes_left,
++                                   folio_size(folio) - offset_in_folio(folio, 0));
++              memcpy(fit_c, pre_fit, bytes_to_copy);
++              fit_c += bytes_to_copy;
++              bytes_left -= bytes_to_copy;
++              if (bytes_left) {
++                      folio_put(folio);
++                      folio = read_mapping_folio(mapping, f_index++, NULL);
++                      if (IS_ERR(folio)) {
++                              ret = PTR_ERR(folio);
++                              goto out_blkdev;
++                      };
++                      pre_fit = folio_address(folio) + offset_in_folio(folio, 0);
++              }
++      }
++      folio_put(folio);
++
++      /* set boot config node name U-Boot may have added to the device tree */
++      np = of_find_node_by_path("/chosen");
++      if (np) {
++              bootconf_c = of_get_property(np, "u-boot,bootconf", &bootconf_len);
++              if (bootconf_c && bootconf_len)
++                      bootconf = kmemdup_nul(bootconf_c, bootconf_len, GFP_KERNEL);
++      }
++
++      if (bootconf) {
++              bootconf_term = strchr(bootconf, '#');
++              if (bootconf_term)
++                      *bootconf_term = '\0';
++      }
++
++      /* find configuration path in uImage.FIT */
++      config = fdt_path_offset(fit, FIT_CONFS_PATH);
++      if (config < 0) {
++              pr_err("FIT: Cannot find %s node: %d\n",
++                      FIT_CONFS_PATH, config);
++              ret = -ENOENT;
++              goto out_bootconf;
++      }
++
++      /* get default configuration node name */
++      config_default =
++              fdt_getprop(fit, config, FIT_DEFAULT_PROP, &config_default_len);
++
++      /* make sure we got either default or selected boot config node name */
++      if (!config_default && !bootconf) {
++              pr_err("FIT: Cannot find default configuration\n");
++              ret = -ENOENT;
++              goto out_bootconf;
++      }
++
++      /* find selected boot config node, fallback on default config node */
++      node = fdt_subnode_offset(fit, config, bootconf ?: config_default);
++      if (node < 0) {
++              pr_err("FIT: Cannot find %s node: %d\n",
++                      bootconf ?: config_default, node);
++              ret = -ENOENT;
++              goto out_bootconf;
++      }
++
++      pr_info("FIT: Detected U-Boot %s\n", ubootver);
++
++      /* get selected configuration data */
++      config_description =
++              fdt_getprop(fit, node, FIT_DESC_PROP, &config_description_len);
++      config_loadables = fdt_getprop(fit, node, FIT_LOADABLE_PROP,
++                                     &config_loadables_len);
++
++      pr_info("FIT: %s configuration: \"%.*s\"%s%.*s%s\n",
++              bootconf ? "Selected" : "Default",
++              bootconf ? bootconf_len : config_default_len,
++              bootconf ?: config_default,
++              config_description ? " (" : "",
++              config_description ? config_description_len : 0,
++              config_description ?: "",
++              config_description ? ")" : "");
++
++      if (!config_loadables || !config_loadables_len) {
++              pr_err("FIT: No loadables configured in \"%s\"\n",
++                      bootconf ?: config_default);
++              ret = -ENOENT;
++              goto out_bootconf;
++      }
++
++      /* get images path in uImage.FIT */
++      images = fdt_path_offset(fit, FIT_IMAGES_PATH);
++      if (images < 0) {
++              pr_err("FIT: Cannot find %s node: %d\n", FIT_IMAGES_PATH, images);
++              ret = -EINVAL;
++              goto out_bootconf;
++      }
++
++      /* iterate over images in uImage.FIT */
++      fdt_for_each_subnode(node, fit, images) {
++              image_name = fdt_get_name(fit, node, &image_name_len);
++              image_type = fdt_getprop(fit, node, FIT_TYPE_PROP, &image_type_len);
++              image_offset_be = fdt_getprop(fit, node, FIT_DATA_OFFSET_PROP, NULL);
++              image_pos_be = fdt_getprop(fit, node, FIT_DATA_POSITION_PROP, NULL);
++              image_len_be = fdt_getprop(fit, node, FIT_DATA_SIZE_PROP, NULL);
++
++              if (!image_name || !image_type || !image_len_be ||
++                  !image_name_len || !image_type_len)
++                      continue;
++
++              image_len = be32_to_cpu(*image_len_be);
++              if (!image_len)
++                      continue;
++
++              if (image_offset_be)
++                      image_pos = be32_to_cpu(*image_offset_be) + size;
++              else if (image_pos_be)
++                      image_pos = be32_to_cpu(*image_pos_be);
++              else
++                      continue;
++
++              image_description = fdt_getprop(fit, node, FIT_DESC_PROP,
++                                              &image_description_len);
++
++              pr_info("FIT: %16s sub-image 0x%08x..0x%08x \"%.*s\"%s%.*s%s\n",
++                      image_type, image_pos, image_pos + image_len - 1,
++                      image_name_len, image_name, image_description ? " (" : "",
++                      image_description ? image_description_len : 0,
++                      image_description ?: "", image_description ? ") " : "");
++
++              /* only 'filesystem' images should be mapped as partitions */
++              if (strncmp(image_type, FIT_FILESYSTEM_PROP, image_type_len))
++                      continue;
++
++              /* check if sub-image is part of configured loadables */
++              found = false;
++              loadable = config_loadables;
++              loadables_rem_len = config_loadables_len;
++              for (loadcnt = 0; loadables_rem_len > 1 &&
++                                loadcnt < MAX_FIT_LOADABLES; ++loadcnt) {
++                      loadable_len =
++                              strnlen(loadable, loadables_rem_len - 1) + 1;
++                      loadables_rem_len -= loadable_len;
++                      if (!strncmp(image_name, loadable, loadable_len)) {
++                              found = true;
++                              break;
++                      }
++                      loadable += loadable_len;
++              }
++              if (!found)
++                      continue;
++
++              if (image_pos % (1 << PAGE_SHIFT)) {
++                      dev_err(dev, "FIT: image %.*s start not aligned to page boundaries, skipping\n",
++                              image_name_len, image_name);
++                      continue;
++              }
++
++              if (image_len % (1 << PAGE_SHIFT)) {
++                      dev_err(dev, "FIT: sub-image %.*s end not aligned to page boundaries, skipping\n",
++                              image_name_len, image_name);
++                      continue;
++              }
++
++              start_sect = image_pos >> SECTOR_SHIFT;
++              nr_sects = image_len >> SECTOR_SHIFT;
++              imgmaxsect = max_t(sector_t, imgmaxsect, start_sect + nr_sects);
++
++              if (start_sect + nr_sects > dsectors) {
++                      dev_err(dev, "FIT: sub-image %.*s disk access beyond EOD\n",
++                              image_name_len, image_name);
++                      continue;
++              }
++
++              if (!slot) {
++                      ret = sysfs_create_link_nowarn(&pdev->dev.kobj, bdev_kobj(bdev), "lower_dev");
++                      if (ret && ret != -EEXIST)
++                              goto out_bootconf;
++
++                      ret = 0;
++              }
++
++              add_fit_subimage_device(bdev, slot++, start_sect, nr_sects, true);
++      }
++
++      if (!found || !slot)
++              goto out_bootconf;
++
++      dev_info(dev, "mapped %u uImage.FIT filesystem sub-image%s as /dev/fit%s%u%s\n",
++               slot, (slot > 1)?"s":"", (slot > 1)?"[0...":"", slot - 1,
++               (slot > 1)?"]":"");
++
++      /* in case uImage.FIT is stored in a partition, map the remaining space */
++      if (!bdev->bd_read_only && bdev_is_partition(bdev) &&
++          (imgmaxsect + MIN_FREE_SECT) < dsectors) {
++              add_fit_subimage_device(bdev, slot++, imgmaxsect,
++                                      dsectors - imgmaxsect, false);
++              dev_info(dev, "mapped remaining space as /dev/fitrw\n");
++      }
++
++out_bootconf:
++      kfree(bootconf);
++      kfree(fit);
++out_blkdev:
++      if (!found || ret)
++              blkdev_put(bdev, &_fitblk_claim_ptr);
++
++      return ret;
++}
++
++static int fitblk_match_of_node(struct device *dev, const void *np)
++{
++      int ret;
++
++      ret = device_match_of_node(dev, np);
++      if (ret)
++              return ret;
++
++      /*
++       * To match ubiblock and mtdblock devices by their parent ubi
++       * or mtd device, also consider block device parent
++       */
++      if (!dev->parent)
++              return 0;
++
++      return device_match_of_node(dev->parent, np);
++}
++
++static int fitblk_probe(struct platform_device *pdev)
++{
++      struct device *dev;
++
++      dev = class_find_device(&block_class, NULL, rootdisk, fitblk_match_of_node);
++      if (!dev)
++              return -EPROBE_DEFER;
++
++      return parse_fit_on_dev(dev);
++}
++
++static struct platform_driver fitblk_driver = {
++      .probe          = fitblk_probe,
++      .driver         = {
++              .name   = "fitblk",
++      },
++};
++
++static int __init fitblk_init(void)
++{
++      /* detect U-Boot firmware */
++      ubootver = of_get_property(of_chosen, "u-boot,version", NULL);
++      if (!ubootver)
++              return 0;
++
++      /* parse 'rootdisk' property phandle */
++      rootdisk = of_parse_phandle(of_chosen, "rootdisk", 0);
++      if (!rootdisk)
++              return 0;
++
++      if (platform_driver_register(&fitblk_driver))
++              return -ENODEV;
++
++      refcount_set(&num_devs, 1);
++      pdev = platform_device_register_simple("fitblk", -1, NULL, 0);
++      if (IS_ERR(pdev))
++              return PTR_ERR(pdev);
++
++      return 0;
++}
++device_initcall(fitblk_init);
+--- /dev/null
++++ b/include/uapi/linux/fitblk.h
+@@ -0,0 +1,10 @@
++/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
++#ifndef _UAPI_LINUX_FITBLK_H
++#define _UAPI_LINUX_FITBLK_H
++
++/*
++ * IOCTL commands --- we will commandeer 0x46 ('F')
++ */
++#define FITBLK_RELEASE             0x4600
++
++#endif /* _UAPI_LINUX_FITBLK_H */
diff --git a/target/linux/generic/pending-6.6/511-init-bypass-device-lookup-for-dev-fit-rootfs.patch b/target/linux/generic/pending-6.6/511-init-bypass-device-lookup-for-dev-fit-rootfs.patch
new file mode 100644 (file)
index 0000000..b2b7f5e
--- /dev/null
@@ -0,0 +1,25 @@
+From 5ede3f8aed9a1a579bf7304142600d1f3500add9 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 12 Jun 2023 03:58:42 +0100
+Subject: [PATCH 2/2] init: bypass device lookup for /dev/fit* rootfs
+
+Allow 'rootwait' as /dev/fit* can show up late if the underlaying
+device is probed late.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ init/do_mounts.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/init/do_mounts.c
++++ b/init/do_mounts.c
+@@ -463,7 +463,8 @@ static dev_t __init parse_root_device(ch
+       int error;
+       dev_t dev;
+-      if (!strncmp(root_device_name, "mtd", 3) ||
++      if (!strncmp(root_device_name, "fit", 3) ||
++          !strncmp(root_device_name, "mtd", 3) ||
+           !strncmp(root_device_name, "ubi", 3))
+               return Root_Generic;
+       if (strcmp(root_device_name, "/dev/nfs") == 0)
diff --git a/target/linux/generic/pending-6.6/530-jffs2_make_lzma_available.patch b/target/linux/generic/pending-6.6/530-jffs2_make_lzma_available.patch
new file mode 100644 (file)
index 0000000..66c458d
--- /dev/null
@@ -0,0 +1,5190 @@
+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
+@@ -381,6 +381,9 @@ int __init jffs2_compressors_init(void)
+       ret = jffs2_lzo_init();
+       if (ret)
+               goto exit_dynrubin;
++      ret = jffs2_lzma_init();
++      if (ret)
++              goto exit_lzo;
+ /* Setting default compression mode */
+@@ -402,6 +405,8 @@ int __init jffs2_compressors_init(void)
+ #endif
+       return 0;
++exit_lzo:
++      jffs2_lzo_exit();
+ exit_dynrubin:
+       jffs2_dynrubin_exit();
+ exit_runinmips:
+@@ -417,6 +422,7 @@ exit:
+ int jffs2_compressors_exit(void)
+ {
+ /* Unregistering compressors */
++      jffs2_lzma_exit();
+       jffs2_lzo_exit();
+       jffs2_dynrubin_exit();
+       jffs2_rubinmips_exit();
+--- 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 */
+@@ -115,5 +115,12 @@ extern void jffs2_lzo_exit(void);
+ static inline int jffs2_lzo_init(void) { return 0; }
+ static inline void jffs2_lzo_exit(void) {}
+ #endif
++#ifdef CONFIG_JFFS2_LZMA
++extern int jffs2_lzma_init(void);
++extern void jffs2_lzma_exit(void);
++#else
++static inline int jffs2_lzma_init(void) { return 0; }
++static inline 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
+@@ -375,14 +375,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 = { .Alloc = p_lzma_malloc, .Free = 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
+@@ -356,6 +356,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
+@@ -145,6 +145,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)
+@@ -205,6 +215,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/target/linux/generic/pending-6.6/532-jffs2_eofdetect.patch b/target/linux/generic/pending-6.6/532-jffs2_eofdetect.patch
new file mode 100644 (file)
index 0000000..744fbd0
--- /dev/null
@@ -0,0 +1,65 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: fs: jffs2: EOF marker
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ fs/jffs2/build.c | 10 ++++++++++
+ fs/jffs2/scan.c  | 21 +++++++++++++++++++--
+ 2 files changed, 29 insertions(+), 2 deletions(-)
+
+--- a/fs/jffs2/build.c
++++ b/fs/jffs2/build.c
+@@ -117,6 +117,16 @@ static int jffs2_build_filesystem(struct
+       dbg_fsbuild("scanned flash completely\n");
+       jffs2_dbg_dump_block_lists_nolock(c);
++      if (c->flags & (1 << 7)) {
++              printk("%s(): unlocking the mtd device... ", __func__);
++              mtd_unlock(c->mtd, 0, c->mtd->size);
++              printk("done.\n");
++
++              printk("%s(): erasing all blocks after the end marker... ", __func__);
++              jffs2_erase_pending_blocks(c, -1);
++              printk("done.\n");
++      }
++
+       dbg_fsbuild("pass 1 starting\n");
+       c->flags |= JFFS2_SB_FLAG_BUILDING;
+       /* Now scan the directory tree, increasing nlink according to every dirent found. */
+--- a/fs/jffs2/scan.c
++++ b/fs/jffs2/scan.c
+@@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_in
+               /* reset summary info for next eraseblock scan */
+               jffs2_sum_reset_collected(s);
+-              ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
+-                                              buf_size, s);
++              if (c->flags & (1 << 7)) {
++                      if (mtd_block_isbad(c->mtd, jeb->offset))
++                              ret = BLK_STATE_BADBLOCK;
++                      else
++                              ret = BLK_STATE_ALLFF;
++              } else
++                      ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
++                                                      buf_size, s);
+               if (ret < 0)
+                       goto out;
+@@ -567,6 +573,17 @@ full_scan:
+                       return err;
+       }
++      if ((buf[0] == 0xde) &&
++              (buf[1] == 0xad) &&
++              (buf[2] == 0xc0) &&
++              (buf[3] == 0xde)) {
++              /* end of filesystem. erase everything after this point */
++              printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset);
++              c->flags |= (1 << 7);
++
++              return BLK_STATE_ALLFF;
++      }
++
+       /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
+       ofs = 0;
+       max_ofs = EMPTY_SCAN_SIZE(c->sector_size);
diff --git a/target/linux/generic/pending-6.6/600-netfilter_conntrack_flush.patch b/target/linux/generic/pending-6.6/600-netfilter_conntrack_flush.patch
new file mode 100644 (file)
index 0000000..52e97e4
--- /dev/null
@@ -0,0 +1,90 @@
+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>
+@@ -458,6 +459,58 @@ 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 nf_ct_iter_data iter_data;
++      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;
++      }
++
++      iter_data.net = seq_file_net(seq);
++      iter_data.data = &kr;
++      nf_ct_iterate_cleanup_net(kill_matching, &iter_data);
++
++      return 0;
++}
++
+ static const struct seq_operations ct_cpu_seq_ops = {
+       .start  = ct_cpu_seq_start,
+       .next   = ct_cpu_seq_next,
+@@ -471,8 +524,9 @@ static int nf_conntrack_standalone_init_
+       kuid_t root_uid;
+       kgid_t root_gid;
+-      pde = proc_create_net("nf_conntrack", 0440, net->proc_net, &ct_seq_ops,
+-                      sizeof(struct ct_iter_state));
++      pde = proc_create_net_data_write("nf_conntrack", 0440, net->proc_net,
++                                       &ct_seq_ops, &ct_file_write,
++                                       sizeof(struct ct_iter_state), NULL);
+       if (!pde)
+               goto out_nf_conntrack;
diff --git a/target/linux/generic/pending-6.6/610-netfilter_match_bypass_default_checks.patch b/target/linux/generic/pending-6.6/610-netfilter_match_bypass_default_checks.patch
new file mode 100644 (file)
index 0000000..fd22200
--- /dev/null
@@ -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
+@@ -48,6 +48,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,
+@@ -78,6 +81,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)
+ {
+@@ -523,6 +549,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;
+@@ -817,6 +845,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))
+@@ -844,6 +873,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) {
+@@ -1225,12 +1262,15 @@ compat_copy_entry_to_user(struct ipt_ent
+       compat_uint_t origsize;
+       const struct xt_entry_match *ematch;
+       int ret = 0;
++      u8 flags = e->ip.flags & IPT_F_MASK;
+       origsize = *size;
+       ce = *dstptr;
+       if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 ||
+           copy_to_user(&ce->counters, &counters[i],
+-          sizeof(counters[i])) != 0)
++          sizeof(counters[i])) != 0 ||
++          copy_to_user(&ce->ip.flags, &flags,
++          sizeof(flags)) != 0)
+               return -EFAULT;
+       *dstptr += sizeof(struct compat_ipt_entry);
diff --git a/target/linux/generic/pending-6.6/611-netfilter_match_bypass_default_table.patch b/target/linux/generic/pending-6.6/611-netfilter_match_bypass_default_table.patch
new file mode 100644 (file)
index 0000000..9f0efe4
--- /dev/null
@@ -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
+@@ -244,6 +244,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(void *priv,
+@@ -265,27 +292,28 @@ ipt_do_table(void *priv,
+       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.
+@@ -298,7 +326,16 @@ ipt_do_table(void *priv,
+       if (static_key_false(&xt_tee_enabled))
+               jumpstack += private->stacksize * __this_cpu_read(nf_skb_duplicated);
+-      e = get_entry(table_base, private->hook_entry[hook]);
++      /* We handle fragments by dealing with the first fragment as
++       * if it was a normal packet.  All other fragments are treated
++       * normally, except that they will NEVER match rules that ask
++       * things we don't know, ie. tcp syn flag or ports).  If the
++       * rule is also a fragment-specific rule, non-fragments won't
++       * match it. */
++      acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
++      acpar.thoff   = ip_hdrlen(skb);
++      acpar.hotdrop = false;
++      acpar.state   = state;
+       do {
+               const struct xt_entry_target *t;
diff --git a/target/linux/generic/pending-6.6/612-netfilter_match_reduce_memory_access.patch b/target/linux/generic/pending-6.6/612-netfilter_match_reduce_memory_access.patch
new file mode 100644 (file)
index 0000000..7f291fc
--- /dev/null
@@ -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
+@@ -51,9 +51,9 @@ ip_packet_match(const struct iphdr *ip,
+       if (ipinfo->flags & IPT_F_NO_DEF_MATCH)
+               return true;
+-      if (NF_INVF(ipinfo, IPT_INV_SRCIP,
++      if (NF_INVF(ipinfo, IPT_INV_SRCIP, ipinfo->smsk.s_addr &&
+                   (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) ||
+-          NF_INVF(ipinfo, IPT_INV_DSTIP,
++          NF_INVF(ipinfo, IPT_INV_DSTIP, ipinfo->dmsk.s_addr &&
+                   (ip->daddr & ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr))
+               return false;
diff --git a/target/linux/generic/pending-6.6/620-net_sched-codel-do-not-defer-queue-length-update.patch b/target/linux/generic/pending-6.6/620-net_sched-codel-do-not-defer-queue-length-update.patch
new file mode 100644 (file)
index 0000000..4b4825a
--- /dev/null
@@ -0,0 +1,86 @@
+From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
+Date: Mon, 21 Aug 2017 11:14:14 +0300
+Subject: [PATCH] net_sched/codel: do not defer queue length update
+
+When codel wants to drop last packet in ->dequeue() it cannot call
+qdisc_tree_reduce_backlog() right away - it will notify parent qdisc
+about zero qlen and HTB/HFSC will deactivate class. The same class will
+be deactivated second time by caller of ->dequeue(). Currently codel and
+fq_codel defer update. This triggers warning in HFSC when it's qlen != 0
+but there is no active classes.
+
+This patch update parent queue length immediately: just temporary increase
+qlen around qdisc_tree_reduce_backlog() to prevent first class deactivation
+if we have skb to return.
+
+This might open another problem in HFSC - now operation peek could fail and
+deactivate parent class.
+
+Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
+---
+
+--- a/net/sched/sch_codel.c
++++ b/net/sched/sch_codel.c
+@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_deque
+                           &q->stats, qdisc_pkt_len, codel_get_enqueue_time,
+                           drop_func, dequeue_func);
+-      /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
+-       * or HTB crashes. Defer it for next round.
++      /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
++       * parent class, dequeue in parent qdisc will do the same if we
++       * return skb. Temporary increment qlen if we have skb.
+        */
+-      if (q->stats.drop_count && sch->q.qlen) {
+-              qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
++      if (q->stats.drop_count) {
++              if (skb)
++                      sch->q.qlen++;
++              qdisc_tree_reduce_backlog(sch, q->stats.drop_count,
++                                        q->stats.drop_len);
++              if (skb)
++                      sch->q.qlen--;
+               q->stats.drop_count = 0;
+               q->stats.drop_len = 0;
+       }
+--- a/net/sched/sch_fq_codel.c
++++ b/net/sched/sch_fq_codel.c
+@@ -304,6 +304,21 @@ begin:
+                           &flow->cvars, &q->cstats, qdisc_pkt_len,
+                           codel_get_enqueue_time, drop_func, dequeue_func);
++      /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
++       * parent class, dequeue in parent qdisc will do the same if we
++       * return skb. Temporary increment qlen if we have skb.
++       */
++      if (q->cstats.drop_count) {
++              if (skb)
++                      sch->q.qlen++;
++              qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
++                                        q->cstats.drop_len);
++              if (skb)
++                      sch->q.qlen--;
++              q->cstats.drop_count = 0;
++              q->cstats.drop_len = 0;
++      }
++
+       if (!skb) {
+               /* force a pass through old_flows to prevent starvation */
+               if ((head == &q->new_flows) && !list_empty(&q->old_flows))
+@@ -314,15 +329,6 @@ begin:
+       }
+       qdisc_bstats_update(sch, skb);
+       flow->deficit -= qdisc_pkt_len(skb);
+-      /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
+-       * or HTB crashes. Defer it for next round.
+-       */
+-      if (q->cstats.drop_count && sch->q.qlen) {
+-              qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
+-                                        q->cstats.drop_len);
+-              q->cstats.drop_count = 0;
+-              q->cstats.drop_len = 0;
+-      }
+       return skb;
+ }
diff --git a/target/linux/generic/pending-6.6/630-packet_socket_type.patch b/target/linux/generic/pending-6.6/630-packet_socket_type.patch
new file mode 100644 (file)
index 0000000..2b753ef
--- /dev/null
@@ -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 */
+@@ -60,6 +62,7 @@ struct sockaddr_ll {
+ #define PACKET_FANOUT_DATA            22
+ #define PACKET_IGNORE_OUTGOING                23
+ #define PACKET_VNET_HDR_SZ            24
++#define PACKET_RECV_TYPE              25
+ #define PACKET_FANOUT_HASH            0
+ #define PACKET_FANOUT_LB              1
+--- a/net/packet/af_packet.c
++++ b/net/packet/af_packet.c
+@@ -1925,6 +1925,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
+@@ -1932,6 +1933,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
+@@ -1944,7 +1946,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)))
+@@ -2190,12 +2192,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;
+@@ -2322,12 +2324,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;
+@@ -3451,6 +3453,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;
+@@ -4118,6 +4121,16 @@ packet_setsockopt(struct socket *sock, i
+               packet_sock_flag_set(po, PACKET_SOCK_QDISC_BYPASS, val);
+               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;
+       }
+@@ -4177,6 +4190,13 @@ static int packet_getsockopt(struct sock
+       case PACKET_VNET_HDR_SZ:
+               val = READ_ONCE(po->vnet_hdr_sz);
+               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
+@@ -131,6 +131,7 @@ struct packet_sock {
+       struct net_device __rcu *cached_dev;
+       struct packet_type      prot_hook ____cacheline_aligned_in_smp;
+       atomic_t                tp_drops ____cacheline_aligned_in_smp;
++      unsigned int            pkt_type;
+ };
+ #define pkt_sk(ptr) container_of_const(ptr, struct packet_sock, sk)
diff --git a/target/linux/generic/pending-6.6/640-net-bridge-fix-switchdev-host-mdb-entry-updates.patch b/target/linux/generic/pending-6.6/640-net-bridge-fix-switchdev-host-mdb-entry-updates.patch
new file mode 100644 (file)
index 0000000..cf55c6a
--- /dev/null
@@ -0,0 +1,42 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 22 Aug 2024 18:02:17 +0200
+Subject: [PATCH] net: bridge: fix switchdev host mdb entry updates
+
+When a mdb entry is removed, the bridge switchdev code can issue a
+switchdev_port_obj_del call for a port that was not offloaded.
+
+This leads to an imbalance in switchdev_port_obj_add/del calls, since
+br_switchdev_mdb_replay has not been called for the port before.
+
+This can lead to potential multicast forwarding issues and messages such as:
+mt7915e 0000:01:00.0 wl1-ap0: Failed to del Host Multicast Database entry
+       (object id=3) with error: -ENOENT (-2).
+
+Fix this issue by checking the port offload status when iterating over
+lower devs.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/bridge/br_switchdev.c
++++ b/net/bridge/br_switchdev.c
+@@ -568,10 +568,18 @@ static void br_switchdev_host_mdb(struct
+                                 struct net_bridge_mdb_entry *mp, int type)
+ {
+       struct net_device *lower_dev;
++      struct net_bridge_port *port;
+       struct list_head *iter;
+-      netdev_for_each_lower_dev(dev, lower_dev, iter)
++      rcu_read_lock();
++      netdev_for_each_lower_dev(dev, lower_dev, iter) {
++              port = br_port_get_rcu(lower_dev);
++              if (!port || !port->offload_count)
++                      continue;
++
+               br_switchdev_host_mdb_one(dev, lower_dev, mp, type);
++      }
++      rcu_read_unlock();
+ }
+ static int
diff --git a/target/linux/generic/pending-6.6/641-net-bridge-switchdev-Don-t-drop-packets-between-port.patch b/target/linux/generic/pending-6.6/641-net-bridge-switchdev-Don-t-drop-packets-between-port.patch
new file mode 100644 (file)
index 0000000..1c4ec61
--- /dev/null
@@ -0,0 +1,38 @@
+From: "Leon M. Busch-George" <leon@georgemail.eu>
+Date: Sun, 20 Oct 2024 18:20:14 +0200
+Subject: [PATCH] net: bridge: switchdev: Don't drop packets between ports with
+ no hwdom
+
+nbp_switchdev_allowed_egress uses hwdom to determine whether or not a
+packet has already been forwarded to a hardware domain. For
+net_bridge_ports that aren't set up to use forward offloading, hwdom is
+set to 0. When both ingress and egress port have no hwdom,
+'cb->src_hwdom != p->hwdom' indicates that the packet is already known in
+the target domain - which it isn't - and the packet is wrongly dropped.
+
+The error was found on a bridge containing a wifi device and a VLAN
+tagging device (e.g. eth0.12). With VLAN filtering, this shouldn't happen.
+
+This patch adds a check for p->hwdom != 0 before comparing hardware
+domains to restore forwarding between ports with hwdom = 0.
+
+fwd_hwdoms are only set for ports with offloading enabled, which also
+implies a valid hwdom, so the check '!test_bit(p->hwdom, &cb->fwd_hwdoms)'
+doesn't fail in this way (yet - fingers crossed..) and it is left in place.
+
+Co-developed-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Leon M. Busch-George <leon@georgemail.eu>
+---
+
+--- a/net/bridge/br_switchdev.c
++++ b/net/bridge/br_switchdev.c
+@@ -67,7 +67,7 @@ bool nbp_switchdev_allowed_egress(const
+       struct br_input_skb_cb *cb = BR_INPUT_SKB_CB(skb);
+       return !test_bit(p->hwdom, &cb->fwd_hwdoms) &&
+-              (!skb->offload_fwd_mark || cb->src_hwdom != p->hwdom);
++              (!skb->offload_fwd_mark || !p->hwdom || cb->src_hwdom != p->hwdom);
+ }
+ /* Flags that can be offloaded to hardware */
diff --git a/target/linux/generic/pending-6.6/655-increase_skb_pad.patch b/target/linux/generic/pending-6.6/655-increase_skb_pad.patch
new file mode 100644 (file)
index 0000000..ce7db56
--- /dev/null
@@ -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
+@@ -3062,7 +3062,7 @@ static inline int pskb_network_may_pull(
+  * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8)
+  */
+ #ifndef NET_SKB_PAD
+-#define NET_SKB_PAD   max(32, L1_CACHE_BYTES)
++#define NET_SKB_PAD   max(64, L1_CACHE_BYTES)
+ #endif
+ int ___pskb_trim(struct sk_buff *skb, unsigned int len);
diff --git a/target/linux/generic/pending-6.6/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch b/target/linux/generic/pending-6.6/666-Add-support-for-MAP-E-FMRs-mesh-mode.patch
new file mode 100644 (file)
index 0000000..863372e
--- /dev/null
@@ -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];
+ }
+@@ -376,6 +395,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
+@@ -788,6 +813,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,
+@@ -855,6 +981,27 @@ static int __ip6_tnl_rcv(struct ip6_tnl
+       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);
+@@ -1002,6 +1149,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
+@@ -1292,6 +1440,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;
+@@ -1391,6 +1540,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;
+@@ -1544,6 +1705,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);
+ }
+@@ -1578,6 +1747,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));
+ }
+@@ -1966,6 +2136,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)
+ {
+@@ -2003,6 +2182,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 int ip6_tnl_newlink(struct net *src_net, struct net_device *dev,
+@@ -2086,6 +2305,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) +
+@@ -2115,6 +2340,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;
+ }
+@@ -2122,6 +2365,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) ||
+@@ -2131,9 +2377,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) ||
+@@ -2173,6 +2437,7 @@ static const struct nla_policy ip6_tnl_p
+       [IFLA_IPTUN_ENCAP_DPORT]        = { .type = NLA_U16 },
+       [IFLA_IPTUN_COLLECT_METADATA]   = { .type = NLA_FLAG },
+       [IFLA_IPTUN_FWMARK]             = { .type = NLA_U32 },
++      [IFLA_IPTUN_FMRS]               = { .type = NLA_NESTED },
+ };
+ static struct rtnl_link_ops ip6_link_ops __read_mostly = {
diff --git a/target/linux/generic/pending-6.6/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/target/linux/generic/pending-6.6/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch
new file mode 100644 (file)
index 0000000..9cf9117
--- /dev/null
@@ -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
+@@ -86,6 +86,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
+@@ -265,6 +265,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
+@@ -145,6 +145,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
+@@ -2784,6 +2784,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
+@@ -190,6 +190,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
+@@ -221,6 +221,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
+@@ -180,6 +180,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
+@@ -98,6 +98,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,
+@@ -318,6 +320,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 = {
++              .__rcuref       = RCUREF_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 = {
+               .__rcuref       = RCUREF_INIT(1),
+@@ -1043,6 +1057,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,
+@@ -1078,6 +1093,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:
+@@ -4546,6 +4565,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.
+  */
+@@ -5037,7 +5067,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)
+@@ -6284,6 +6315,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
+@@ -6295,6 +6328,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
+       }
+@@ -6495,6 +6529,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);
+@@ -6505,11 +6541,21 @@ static int __net_init ip6_route_net_init
+                        ip6_template_metrics, true);
+       INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->dst.rt_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->dst.rt_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);
+@@ -6536,6 +6582,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:
+@@ -6555,6 +6603,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);
+@@ -6638,6 +6687,9 @@ void __init ip6_route_init_special_entri
+       init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
+       init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
+       init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
++      init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev;
++      init_net.ipv6.ip6_policy_failed_entry->rt6i_idev =
++              in6_dev_get(init_net.loopback_dev);
+   #endif
+ }
diff --git a/target/linux/generic/pending-6.6/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch b/target/linux/generic/pending-6.6/671-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch
new file mode 100644 (file)
index 0000000..94416a5
--- /dev/null
@@ -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
+@@ -269,6 +269,8 @@ enum {
+       __RTN_MAX
+ };
++#define RTN_FAILED_POLICY RTN_POLICY_FAILED
++
+ #define RTN_MAX (__RTN_MAX - 1)
diff --git a/target/linux/generic/pending-6.6/680-net-add-TCP-fraglist-GRO-support.patch b/target/linux/generic/pending-6.6/680-net-add-TCP-fraglist-GRO-support.patch
new file mode 100644 (file)
index 0000000..b810e7e
--- /dev/null
@@ -0,0 +1,578 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 23 Apr 2024 11:23:03 +0200
+Subject: [PATCH] net: add TCP fraglist GRO support
+
+When forwarding TCP after GRO, software segmentation is very expensive,
+especially when the checksum needs to be recalculated.
+One case where that's currently unavoidable is when routing packets over
+PPPoE. Performance improves significantly when using fraglist GRO
+implemented in the same way as for UDP.
+
+Here's a measurement of running 2 TCP streams through a MediaTek MT7622
+device (2-core Cortex-A53), which runs NAT with flow offload enabled from
+one ethernet port to PPPoE on another ethernet port + cake qdisc set to
+1Gbps.
+
+rx-gro-list off: 630 Mbit/s, CPU 35% idle
+rx-gro-list on:  770 Mbit/s, CPU 40% idle
+
+Signe-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/gro.h
++++ b/include/net/gro.h
+@@ -439,6 +439,7 @@ static inline __wsum ip6_gro_compute_pse
+ }
+ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb);
++int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb);
+ /* Pass the currently batched GRO_NORMAL SKBs up to the stack. */
+ static inline void gro_normal_list(struct napi_struct *napi)
+--- a/include/net/tcp.h
++++ b/include/net/tcp.h
+@@ -2084,7 +2084,10 @@ void tcp_v4_destroy_sock(struct sock *sk
+ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
+                               netdev_features_t features);
+-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb);
++struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb);
++struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th);
++struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
++                              struct tcphdr *th);
+ INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff));
+ INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb));
+ INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *skb, int thoff));
+--- a/net/core/gro.c
++++ b/net/core/gro.c
+@@ -228,6 +228,33 @@ done:
+       return 0;
+ }
++int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
++{
++      if (unlikely(p->len + skb->len >= 65536))
++              return -E2BIG;
++
++      if (NAPI_GRO_CB(p)->last == p)
++              skb_shinfo(p)->frag_list = skb;
++      else
++              NAPI_GRO_CB(p)->last->next = skb;
++
++      skb_pull(skb, skb_gro_offset(skb));
++
++      NAPI_GRO_CB(p)->last = skb;
++      NAPI_GRO_CB(p)->count++;
++      p->data_len += skb->len;
++
++      /* sk ownership - if any - completely transferred to the aggregated packet */
++      skb->destructor = NULL;
++      skb->sk = NULL;
++      p->truesize += skb->truesize;
++      p->len += skb->len;
++
++      NAPI_GRO_CB(skb)->same_flow = 1;
++
++      return 0;
++}
++
+ static void napi_gro_complete(struct napi_struct *napi, struct sk_buff *skb)
+ {
+--- a/net/ipv4/tcp_offload.c
++++ b/net/ipv4/tcp_offload.c
+@@ -28,6 +28,70 @@ static void tcp_gso_tstamp(struct sk_buf
+       }
+ }
++static void __tcpv4_gso_segment_csum(struct sk_buff *seg,
++                                   __be32 *oldip, __be32 newip,
++                                   __be16 *oldport, __be16 newport)
++{
++      struct tcphdr *th;
++      struct iphdr *iph;
++
++      if (*oldip == newip && *oldport == newport)
++              return;
++
++      th = tcp_hdr(seg);
++      iph = ip_hdr(seg);
++
++      inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true);
++      inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
++      *oldport = newport;
++
++      csum_replace4(&iph->check, *oldip, newip);
++      *oldip = newip;
++}
++
++static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs)
++{
++      const struct tcphdr *th;
++      const struct iphdr *iph;
++      struct sk_buff *seg;
++      struct tcphdr *th2;
++      struct iphdr *iph2;
++
++      seg = segs;
++      th = tcp_hdr(seg);
++      iph = ip_hdr(seg);
++      th2 = tcp_hdr(seg->next);
++      iph2 = ip_hdr(seg->next);
++
++      if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
++          iph->daddr == iph2->daddr && iph->saddr == iph2->saddr)
++              return segs;
++
++      while ((seg = seg->next)) {
++              th2 = tcp_hdr(seg);
++              iph2 = ip_hdr(seg);
++
++              __tcpv4_gso_segment_csum(seg,
++                                       &iph2->saddr, iph->saddr,
++                                       &th2->source, th->source);
++              __tcpv4_gso_segment_csum(seg,
++                                       &iph2->daddr, iph->daddr,
++                                       &th2->dest, th->dest);
++      }
++
++      return segs;
++}
++
++static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb,
++                                            netdev_features_t features)
++{
++      skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
++      if (IS_ERR(skb))
++              return skb;
++
++      return __tcpv4_gso_segment_list_csum(skb);
++}
++
+ static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
+                                       netdev_features_t features)
+ {
+@@ -37,6 +101,9 @@ static struct sk_buff *tcp4_gso_segment(
+       if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
+               return ERR_PTR(-EINVAL);
++      if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
++              return __tcp4_gso_segment_list(skb, features);
++
+       if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
+               const struct iphdr *iph = ip_hdr(skb);
+               struct tcphdr *th = tcp_hdr(skb);
+@@ -181,61 +248,76 @@ out:
+       return segs;
+ }
+-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb)
++struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th)
+ {
+-      struct sk_buff *pp = NULL;
++      struct tcphdr *th2;
+       struct sk_buff *p;
++
++      list_for_each_entry(p, head, list) {
++              if (!NAPI_GRO_CB(p)->same_flow)
++                      continue;
++
++              th2 = tcp_hdr(p);
++              if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
++                      NAPI_GRO_CB(p)->same_flow = 0;
++                      continue;
++              }
++
++              return p;
++      }
++
++      return NULL;
++}
++
++struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb)
++{
++      unsigned int thlen, hlen, off;
+       struct tcphdr *th;
+-      struct tcphdr *th2;
+-      unsigned int len;
+-      unsigned int thlen;
+-      __be32 flags;
+-      unsigned int mss = 1;
+-      unsigned int hlen;
+-      unsigned int off;
+-      int flush = 1;
+-      int i;
+       off = skb_gro_offset(skb);
+       hlen = off + sizeof(*th);
+       th = skb_gro_header(skb, hlen, off);
+       if (unlikely(!th))
+-              goto out;
++              return NULL;
+       thlen = th->doff * 4;
+       if (thlen < sizeof(*th))
+-              goto out;
++              return NULL;
+       hlen = off + thlen;
+       if (skb_gro_header_hard(skb, hlen)) {
+               th = skb_gro_header_slow(skb, hlen, off);
+               if (unlikely(!th))
+-                      goto out;
++                      return NULL;
+       }
+       skb_gro_pull(skb, thlen);
+-      len = skb_gro_len(skb);
+-      flags = tcp_flag_word(th);
+-
+-      list_for_each_entry(p, head, list) {
+-              if (!NAPI_GRO_CB(p)->same_flow)
+-                      continue;
++      return th;
++}
+-              th2 = tcp_hdr(p);
++struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
++                              struct tcphdr *th)
++{
++      unsigned int thlen = th->doff * 4;
++      struct sk_buff *pp = NULL;
++      struct sk_buff *p;
++      struct tcphdr *th2;
++      unsigned int len;
++      __be32 flags;
++      unsigned int mss = 1;
++      int flush = 1;
++      int i;
+-              if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
+-                      NAPI_GRO_CB(p)->same_flow = 0;
+-                      continue;
+-              }
++      len = skb_gro_len(skb);
++      flags = tcp_flag_word(th);
+-              goto found;
+-      }
+-      p = NULL;
+-      goto out_check_final;
++      p = tcp_gro_lookup(head, th);
++      if (!p)
++              goto out_check_final;
+-found:
+       /* Include the IP ID check below from the inner most IP hdr */
++      th2 = tcp_hdr(p);
+       flush = NAPI_GRO_CB(p)->flush;
+       flush |= (__force int)(flags & TCP_FLAG_CWR);
+       flush |= (__force int)((flags ^ tcp_flag_word(th2)) &
+@@ -272,6 +354,19 @@ found:
+       flush |= p->decrypted ^ skb->decrypted;
+ #endif
++      if (unlikely(NAPI_GRO_CB(p)->is_flist)) {
++              flush |= (__force int)(flags ^ tcp_flag_word(th2));
++              flush |= skb->ip_summed != p->ip_summed;
++              flush |= skb->csum_level != p->csum_level;
++              flush |= !pskb_may_pull(skb, skb_gro_offset(skb));
++              flush |= NAPI_GRO_CB(p)->count >= 64;
++
++              if (flush || skb_gro_receive_list(p, skb))
++                      mss = 1;
++
++              goto out_check_final;
++      }
++
+       if (flush || skb_gro_receive(p, skb)) {
+               mss = 1;
+               goto out_check_final;
+@@ -293,7 +388,6 @@ out_check_final:
+       if (p && (!NAPI_GRO_CB(skb)->same_flow || flush))
+               pp = p;
+-out:
+       NAPI_GRO_CB(skb)->flush |= (flush != 0);
+       return pp;
+@@ -317,18 +411,58 @@ void tcp_gro_complete(struct sk_buff *sk
+ }
+ EXPORT_SYMBOL(tcp_gro_complete);
++static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
++                                  struct tcphdr *th)
++{
++      const struct iphdr *iph;
++      struct sk_buff *p;
++      struct sock *sk;
++      struct net *net;
++      int iif, sdif;
++
++      if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST))
++              return;
++
++      p = tcp_gro_lookup(head, th);
++      if (p) {
++              NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
++              return;
++      }
++
++      inet_get_iif_sdif(skb, &iif, &sdif);
++      iph = skb_gro_network_header(skb);
++      net = dev_net(skb->dev);
++      sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
++                                     iph->saddr, th->source,
++                                     iph->daddr, ntohs(th->dest),
++                                     iif, sdif);
++      NAPI_GRO_CB(skb)->is_flist = !sk;
++      if (sk)
++              sock_put(sk);
++}
++
+ INDIRECT_CALLABLE_SCOPE
+ struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
+ {
++      struct tcphdr *th;
++
+       /* Don't bother verifying checksum if we're going to flush anyway. */
+       if (!NAPI_GRO_CB(skb)->flush &&
+           skb_gro_checksum_validate(skb, IPPROTO_TCP,
+-                                    inet_gro_compute_pseudo)) {
+-              NAPI_GRO_CB(skb)->flush = 1;
+-              return NULL;
+-      }
++                                    inet_gro_compute_pseudo))
++              goto flush;
++
++      th = tcp_gro_pull_header(skb);
++      if (!th)
++              goto flush;
+-      return tcp_gro_receive(head, skb);
++      tcp4_check_fraglist_gro(head, skb, th);
++
++      return tcp_gro_receive(head, skb, th);
++
++flush:
++      NAPI_GRO_CB(skb)->flush = 1;
++      return NULL;
+ }
+ INDIRECT_CALLABLE_SCOPE int tcp4_gro_complete(struct sk_buff *skb, int thoff)
+@@ -336,6 +470,15 @@ INDIRECT_CALLABLE_SCOPE int tcp4_gro_com
+       const struct iphdr *iph = ip_hdr(skb);
+       struct tcphdr *th = tcp_hdr(skb);
++      if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
++              skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV4;
++              skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
++
++              __skb_incr_checksum_unnecessary(skb);
++
++              return 0;
++      }
++
+       th->check = ~tcp_v4_check(skb->len - thoff, iph->saddr,
+                                 iph->daddr, 0);
+       skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
+--- a/net/ipv4/udp_offload.c
++++ b/net/ipv4/udp_offload.c
+@@ -470,33 +470,6 @@ out:
+       return segs;
+ }
+-static int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
+-{
+-      if (unlikely(p->len + skb->len >= 65536))
+-              return -E2BIG;
+-
+-      if (NAPI_GRO_CB(p)->last == p)
+-              skb_shinfo(p)->frag_list = skb;
+-      else
+-              NAPI_GRO_CB(p)->last->next = skb;
+-
+-      skb_pull(skb, skb_gro_offset(skb));
+-
+-      NAPI_GRO_CB(p)->last = skb;
+-      NAPI_GRO_CB(p)->count++;
+-      p->data_len += skb->len;
+-
+-      /* sk ownership - if any - completely transferred to the aggregated packet */
+-      skb->destructor = NULL;
+-      skb->sk = NULL;
+-      p->truesize += skb->truesize;
+-      p->len += skb->len;
+-
+-      NAPI_GRO_CB(skb)->same_flow = 1;
+-
+-      return 0;
+-}
+-
+ #define UDP_GRO_CNT_MAX 64
+ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
+--- a/net/ipv6/tcpv6_offload.c
++++ b/net/ipv6/tcpv6_offload.c
+@@ -7,24 +7,67 @@
+  */
+ #include <linux/indirect_call_wrapper.h>
+ #include <linux/skbuff.h>
++#include <net/inet6_hashtables.h>
+ #include <net/gro.h>
+ #include <net/protocol.h>
+ #include <net/tcp.h>
+ #include <net/ip6_checksum.h>
+ #include "ip6_offload.h"
++static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
++                                  struct tcphdr *th)
++{
++#if IS_ENABLED(CONFIG_IPV6)
++      const struct ipv6hdr *hdr;
++      struct sk_buff *p;
++      struct sock *sk;
++      struct net *net;
++      int iif, sdif;
++
++      if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST))
++              return;
++
++      p = tcp_gro_lookup(head, th);
++      if (p) {
++              NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
++              return;
++      }
++
++      inet6_get_iif_sdif(skb, &iif, &sdif);
++      hdr = skb_gro_network_header(skb);
++      net = dev_net(skb->dev);
++      sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
++                                      &hdr->saddr, th->source,
++                                      &hdr->daddr, ntohs(th->dest),
++                                      iif, sdif);
++      NAPI_GRO_CB(skb)->is_flist = !sk;
++      if (sk)
++              sock_put(sk);
++#endif /* IS_ENABLED(CONFIG_IPV6) */
++}
++
+ INDIRECT_CALLABLE_SCOPE
+ struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
+ {
++      struct tcphdr *th;
++
+       /* Don't bother verifying checksum if we're going to flush anyway. */
+       if (!NAPI_GRO_CB(skb)->flush &&
+           skb_gro_checksum_validate(skb, IPPROTO_TCP,
+-                                    ip6_gro_compute_pseudo)) {
+-              NAPI_GRO_CB(skb)->flush = 1;
+-              return NULL;
+-      }
++                                    ip6_gro_compute_pseudo))
++              goto flush;
+-      return tcp_gro_receive(head, skb);
++      th = tcp_gro_pull_header(skb);
++      if (!th)
++              goto flush;
++
++      tcp6_check_fraglist_gro(head, skb, th);
++
++      return tcp_gro_receive(head, skb, th);
++
++flush:
++      NAPI_GRO_CB(skb)->flush = 1;
++      return NULL;
+ }
+ INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
+@@ -32,6 +75,15 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
+       const struct ipv6hdr *iph = ipv6_hdr(skb);
+       struct tcphdr *th = tcp_hdr(skb);
++      if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
++              skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV6;
++              skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
++
++              __skb_incr_checksum_unnecessary(skb);
++
++              return 0;
++      }
++
+       th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
+                                 &iph->daddr, 0);
+       skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
+@@ -40,6 +92,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
+       return 0;
+ }
++static void __tcpv6_gso_segment_csum(struct sk_buff *seg,
++                                   __be16 *oldport, __be16 newport)
++{
++      struct tcphdr *th;
++
++      if (*oldport == newport)
++              return;
++
++      th = tcp_hdr(seg);
++      inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
++      *oldport = newport;
++}
++
++static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs)
++{
++      const struct tcphdr *th;
++      const struct ipv6hdr *iph;
++      struct sk_buff *seg;
++      struct tcphdr *th2;
++      struct ipv6hdr *iph2;
++
++      seg = segs;
++      th = tcp_hdr(seg);
++      iph = ipv6_hdr(seg);
++      th2 = tcp_hdr(seg->next);
++      iph2 = ipv6_hdr(seg->next);
++
++      if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
++          ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
++          ipv6_addr_equal(&iph->daddr, &iph2->daddr))
++              return segs;
++
++      while ((seg = seg->next)) {
++              th2 = tcp_hdr(seg);
++              iph2 = ipv6_hdr(seg);
++
++              iph2->saddr = iph->saddr;
++              iph2->daddr = iph->daddr;
++              __tcpv6_gso_segment_csum(seg, &th2->source, th->source);
++              __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest);
++      }
++
++      return segs;
++}
++
++static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb,
++                                            netdev_features_t features)
++{
++      skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
++      if (IS_ERR(skb))
++              return skb;
++
++      return __tcpv6_gso_segment_list_csum(skb);
++}
++
+ static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
+                                       netdev_features_t features)
+ {
+@@ -51,6 +158,9 @@ static struct sk_buff *tcp6_gso_segment(
+       if (!pskb_may_pull(skb, sizeof(*th)))
+               return ERR_PTR(-EINVAL);
++      if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
++              return __tcp6_gso_segment_list(skb, features);
++
+       if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
+               const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+               struct tcphdr *th = tcp_hdr(skb);
diff --git a/target/linux/generic/pending-6.6/681-net-remove-NETIF_F_GSO_FRAGLIST-from-NETIF_F_GSO_SOF.patch b/target/linux/generic/pending-6.6/681-net-remove-NETIF_F_GSO_FRAGLIST-from-NETIF_F_GSO_SOF.patch
new file mode 100644 (file)
index 0000000..97e321b
--- /dev/null
@@ -0,0 +1,129 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 15 Aug 2024 21:15:13 +0200
+Subject: [PATCH] net: remove NETIF_F_GSO_FRAGLIST from NETIF_F_GSO_SOFTWARE
+
+Several drivers set NETIF_F_GSO_SOFTWARE, but mangle fraglist GRO packets
+in a way that they can't be properly segmented anymore.
+In order to properly deal with this, remove fraglist GSO from
+NETIF_F_GSO_SOFTWARE and switch to NETIF_F_GSO_SOFTWARE_ALL (which includes
+fraglist GSO) in places where it's safe to add.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/dummy.c
++++ b/drivers/net/dummy.c
+@@ -118,7 +118,7 @@ static void dummy_setup(struct net_devic
+       dev->flags &= ~IFF_MULTICAST;
+       dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
+       dev->features   |= NETIF_F_SG | NETIF_F_FRAGLIST;
+-      dev->features   |= NETIF_F_GSO_SOFTWARE;
++      dev->features   |= NETIF_F_GSO_SOFTWARE_ALL;
+       dev->features   |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX;
+       dev->features   |= NETIF_F_GSO_ENCAP_ALL;
+       dev->hw_features |= dev->features;
+--- a/drivers/net/loopback.c
++++ b/drivers/net/loopback.c
+@@ -176,7 +176,7 @@ static void gen_lo_setup(struct net_devi
+       dev->flags              = IFF_LOOPBACK;
+       dev->priv_flags         |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
+       netif_keep_dst(dev);
+-      dev->hw_features        = NETIF_F_GSO_SOFTWARE;
++      dev->hw_features        = NETIF_F_GSO_SOFTWARE_ALL;
+       dev->features           = NETIF_F_SG | NETIF_F_FRAGLIST
+               | NETIF_F_GSO_SOFTWARE
+               | NETIF_F_HW_CSUM
+--- a/drivers/net/macvlan.c
++++ b/drivers/net/macvlan.c
+@@ -896,7 +896,7 @@ static int macvlan_hwtstamp_set(struct n
+ static struct lock_class_key macvlan_netdev_addr_lock_key;
+ #define ALWAYS_ON_OFFLOADS \
+-      (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | \
++      (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE_ALL | \
+        NETIF_F_GSO_ROBUST | NETIF_F_GSO_ENCAP_ALL)
+ #define ALWAYS_ON_FEATURES (ALWAYS_ON_OFFLOADS | NETIF_F_LLTX)
+--- a/include/linux/netdev_features.h
++++ b/include/linux/netdev_features.h
+@@ -219,13 +219,14 @@ static inline int find_next_netdev_featu
+ /* List of features with software fallbacks. */
+ #define NETIF_F_GSO_SOFTWARE  (NETIF_F_ALL_TSO | NETIF_F_GSO_SCTP |        \
+-                               NETIF_F_GSO_UDP_L4 | NETIF_F_GSO_FRAGLIST)
++                               NETIF_F_GSO_UDP_L4)
++#define NETIF_F_GSO_SOFTWARE_ALL (NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_FRAGLIST)
+ /*
+  * If one device supports one of these features, then enable them
+  * for all in netdev_increment_features.
+  */
+-#define NETIF_F_ONE_FOR_ALL   (NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ROBUST | \
++#define NETIF_F_ONE_FOR_ALL   (NETIF_F_GSO_SOFTWARE_ALL | NETIF_F_GSO_ROBUST | \
+                                NETIF_F_SG | NETIF_F_HIGHDMA |         \
+                                NETIF_F_FRAGLIST | NETIF_F_VLAN_CHALLENGED)
+--- a/net/8021q/vlan.h
++++ b/net/8021q/vlan.h
+@@ -108,7 +108,7 @@ static inline netdev_features_t vlan_tnl
+       netdev_features_t ret;
+       ret = real_dev->hw_enc_features &
+-            (NETIF_F_CSUM_MASK | NETIF_F_GSO_SOFTWARE |
++            (NETIF_F_CSUM_MASK | NETIF_F_GSO_SOFTWARE_ALL |
+              NETIF_F_GSO_ENCAP_ALL);
+       if ((ret & NETIF_F_GSO_ENCAP_ALL) && (ret & NETIF_F_CSUM_MASK))
+--- a/net/8021q/vlan_dev.c
++++ b/net/8021q/vlan_dev.c
+@@ -583,7 +583,7 @@ static int vlan_dev_init(struct net_devi
+               dev->state |= (1 << __LINK_STATE_NOCARRIER);
+       dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG |
+-                         NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE |
++                         NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE_ALL |
+                          NETIF_F_GSO_ENCAP_ALL |
+                          NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC |
+                          NETIF_F_ALL_FCOE;
+@@ -676,7 +676,7 @@ static netdev_features_t vlan_dev_fix_fe
+       if (lower_features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))
+               lower_features |= NETIF_F_HW_CSUM;
+       features = netdev_intersect_features(features, lower_features);
+-      features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_GSO_SOFTWARE);
++      features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_GSO_SOFTWARE_ALL);
+       features |= NETIF_F_LLTX;
+       return features;
+--- a/net/core/sock.c
++++ b/net/core/sock.c
+@@ -2441,7 +2441,7 @@ void sk_setup_caps(struct sock *sk, stru
+       if (sk_is_tcp(sk))
+               sk->sk_route_caps |= NETIF_F_GSO;
+       if (sk->sk_route_caps & NETIF_F_GSO)
+-              sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE;
++              sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE_ALL;
+       if (unlikely(sk->sk_gso_disabled))
+               sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+       if (sk_can_gso(sk)) {
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1996,7 +1996,7 @@ void ieee80211_color_collision_detection
+ /* interface handling */
+ #define MAC80211_SUPPORTED_FEATURES_TX        (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
+                                        NETIF_F_HW_CSUM | NETIF_F_SG | \
+-                                       NETIF_F_HIGHDMA | NETIF_F_GSO_SOFTWARE | \
++                                       NETIF_F_HIGHDMA | NETIF_F_GSO_SOFTWARE_ALL | \
+                                        NETIF_F_HW_TC)
+ #define MAC80211_SUPPORTED_FEATURES_RX        (NETIF_F_RXCSUM)
+ #define MAC80211_SUPPORTED_FEATURES   (MAC80211_SUPPORTED_FEATURES_TX | \
+--- a/net/openvswitch/vport-internal_dev.c
++++ b/net/openvswitch/vport-internal_dev.c
+@@ -110,7 +110,7 @@ static void do_setup(struct net_device *
+       netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST |
+                          NETIF_F_HIGHDMA | NETIF_F_HW_CSUM |
+-                         NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL;
++                         NETIF_F_GSO_SOFTWARE_ALL | NETIF_F_GSO_ENCAP_ALL;
+       netdev->vlan_features = netdev->features;
+       netdev->hw_enc_features = netdev->features;
diff --git a/target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch b/target/linux/generic/pending-6.6/683-of_net-add-mac-address-to-of-tree.patch
new file mode 100644 (file)
index 0000000..0fb02db
--- /dev/null
@@ -0,0 +1,75 @@
+From 8585756342caa6d27008d1ad0c18023e4211a40a Mon Sep 17 00:00:00 2001
+From: OpenWrt community <openwrt-devel@lists.openwrt.org>
+Date: Wed, 13 Jul 2022 12:22:48 +0200
+Subject: [PATCH] of/of_net: write back netdev MAC-address to device-tree
+
+The label-mac logic relies on the mac-address property of a netdev
+devices of-node. However, the mac address can also be stored as a
+different property or read from e.g. an mtd device.
+
+Create this node when reading a mac-address from OF if it does not
+already exist and copy the mac-address used for the device to this
+property. This way, the MAC address can be accessed using procfs.
+
+---
+ net/core/of_net.c | 22 ++++++++++++++++++++++
+ 1 file changed, 22 insertions(+)
+
+--- a/net/core/of_net.c
++++ b/net/core/of_net.c
+@@ -97,6 +97,27 @@ int of_get_mac_address_nvmem(struct devi
+ }
+ EXPORT_SYMBOL(of_get_mac_address_nvmem);
++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
+@@ -132,17 +153,23 @@ int of_get_mac_address(struct device_nod
+       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;
+-      return of_get_mac_address_nvmem(np, addr);
++      ret = of_get_mac_address_nvmem(np, addr);
++      if (ret)
++              return ret;
++
++found:
++      ret = of_add_mac_address(np, addr);
++      return ret;
+ }
+ EXPORT_SYMBOL(of_get_mac_address);
diff --git a/target/linux/generic/pending-6.6/685-net-gso-fix-tcp-fraglist-segmentation-after-pull-fro.patch b/target/linux/generic/pending-6.6/685-net-gso-fix-tcp-fraglist-segmentation-after-pull-fro.patch
new file mode 100644 (file)
index 0000000..235762b
--- /dev/null
@@ -0,0 +1,74 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 26 Sep 2024 10:41:30 +0200
+Subject: [PATCH] net: gso: fix tcp fraglist segmentation after pull from
+ frag_list
+
+Detect tcp gso fraglist skbs with corrupted geometry (see below) and
+pass these to skb_segment instead of skb_segment_list, as the first
+can segment them correctly.
+
+Valid SKB_GSO_FRAGLIST skbs
+- consist of two or more segments
+- the head_skb holds the protocol headers plus first gso_size
+- one or more frag_list skbs hold exactly one segment
+- all but the last must be gso_size
+
+Optional datapath hooks such as NAT and BPF (bpf_skb_pull_data) can
+modify these skbs, breaking these invariants.
+
+In extreme cases they pull all data into skb linear. For TCP, this
+causes a NULL ptr deref in __tcpv4_gso_segment_list_csum at
+tcp_hdr(seg->next).
+
+Detect invalid geometry due to pull, by checking head_skb size.
+Don't just drop, as this may blackhole a destination. Convert to be
+able to pass to regular skb_segment.
+
+Approach and description based on a patch by Willem de Bruijn.
+
+Link: https://lore.kernel.org/netdev/20240428142913.18666-1-shiming.cheng@mediatek.com/
+Link: https://lore.kernel.org/netdev/20240922150450.3873767-1-willemdebruijn.kernel@gmail.com/
+Fixes: bee88cd5bd83 ("net: add support for segmenting TCP fraglist GSO packets")
+Cc: stable@vger.kernel.org
+Cc: Willem de Bruijn <willemb@google.com>
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/ipv4/tcp_offload.c
++++ b/net/ipv4/tcp_offload.c
+@@ -101,8 +101,14 @@ static struct sk_buff *tcp4_gso_segment(
+       if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
+               return ERR_PTR(-EINVAL);
+-      if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
+-              return __tcp4_gso_segment_list(skb, features);
++      if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) {
++              struct tcphdr *th = tcp_hdr(skb);
++
++              if (skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size)
++                      return __tcp4_gso_segment_list(skb, features);
++
++              skb->ip_summed = CHECKSUM_NONE;
++      }
+       if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
+               const struct iphdr *iph = ip_hdr(skb);
+--- a/net/ipv6/tcpv6_offload.c
++++ b/net/ipv6/tcpv6_offload.c
+@@ -158,8 +158,14 @@ static struct sk_buff *tcp6_gso_segment(
+       if (!pskb_may_pull(skb, sizeof(*th)))
+               return ERR_PTR(-EINVAL);
+-      if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
+-              return __tcp6_gso_segment_list(skb, features);
++      if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) {
++              struct tcphdr *th = tcp_hdr(skb);
++
++              if (skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size)
++                      return __tcp6_gso_segment_list(skb, features);
++
++              skb->ip_summed = CHECKSUM_NONE;
++      }
+       if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
+               const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
diff --git a/target/linux/generic/pending-6.6/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch b/target/linux/generic/pending-6.6/700-netfilter-nft_flow_offload-handle-netdevice-events-f.patch
new file mode 100644 (file)
index 0000000..6b93f47
--- /dev/null
@@ -0,0 +1,110 @@
+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
+@@ -662,6 +662,23 @@ static struct pernet_operations nf_flow_
+       .exit_batch = nf_flow_table_pernet_exit,
+ };
++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)
+ {
+       int ret;
+@@ -674,8 +691,14 @@ static int __init nf_flow_table_module_i
+       if (ret)
+               goto out_offload;
++      ret = register_netdevice_notifier(&flow_offload_netdev_notifier);
++      if (ret)
++              goto out_offload_init;
++
+       return 0;
++out_offload_init:
++      nf_flow_table_offload_exit();
+ out_offload:
+       unregister_pernet_subsys(&nf_flow_table_net_ops);
+       return ret;
+@@ -683,6 +706,7 @@ out_offload:
+ static void __exit nf_flow_table_module_exit(void)
+ {
++      unregister_netdevice_notifier(&flow_offload_netdev_notifier);
+       nf_flow_table_offload_exit();
+       unregister_pernet_subsys(&nf_flow_table_net_ops);
+ }
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -480,47 +480,14 @@ static struct nft_expr_type nft_flow_off
+       .owner          = THIS_MODULE,
+ };
+-static int flow_offload_netdev_event(struct notifier_block *this,
+-                                   unsigned long event, void *ptr)
+-{
+-      struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+-
+-      if (event != NETDEV_DOWN)
+-              return NOTIFY_DONE;
+-
+-      nf_flow_table_cleanup(dev);
+-
+-      return NOTIFY_DONE;
+-}
+-
+-static struct notifier_block flow_offload_netdev_notifier = {
+-      .notifier_call  = flow_offload_netdev_event,
+-};
+-
+ static int __init nft_flow_offload_module_init(void)
+ {
+-      int err;
+-
+-      err = register_netdevice_notifier(&flow_offload_netdev_notifier);
+-      if (err)
+-              goto err;
+-
+-      err = nft_register_expr(&nft_flow_offload_type);
+-      if (err < 0)
+-              goto register_expr;
+-
+-      return 0;
+-
+-register_expr:
+-      unregister_netdevice_notifier(&flow_offload_netdev_notifier);
+-err:
+-      return err;
++      return nft_register_expr(&nft_flow_offload_type);
+ }
+ static void __exit nft_flow_offload_module_exit(void)
+ {
+       nft_unregister_expr(&nft_flow_offload_type);
+-      unregister_netdevice_notifier(&flow_offload_netdev_notifier);
+ }
+ module_init(nft_flow_offload_module_init);
diff --git a/target/linux/generic/pending-6.6/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch b/target/linux/generic/pending-6.6/701-netfilter-nf_tables-ignore-EOPNOTSUPP-on-flowtable-d.patch
new file mode 100644 (file)
index 0000000..d136410
--- /dev/null
@@ -0,0 +1,29 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 31 Aug 2023 21:48:38 +0200
+Subject: [PATCH] netfilter: nf_tables: ignore -EOPNOTSUPP on flowtable device
+ offload setup
+
+On many embedded devices, it is common to configure flowtable offloading for
+a mix of different devices, some of which have hardware offload support and
+some of which don't.
+The current code limits the ability of user space to properly set up such a
+configuration by only allowing adding devices with hardware offload support to
+a offload-enabled flowtable.
+Given that offload-enabled flowtables also imply fallback to pure software
+offloading, this limitation makes little sense.
+Fix it by not bailing out when the offload setup returns -EOPNOTSUPP
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -8417,7 +8417,7 @@ static int nft_register_flowtable_net_ho
+               err = flowtable->data.type->setup(&flowtable->data,
+                                                 hook->ops.dev,
+                                                 FLOW_BLOCK_BIND);
+-              if (err < 0)
++              if (err < 0 && err != -EOPNOTSUPP)
+                       goto err_unregister_net_hooks;
+               err = nf_register_net_hook(net, &hook->ops);
diff --git a/target/linux/generic/pending-6.6/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch b/target/linux/generic/pending-6.6/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch
new file mode 100644 (file)
index 0000000..5563bf1
--- /dev/null
@@ -0,0 +1,21 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 21 Mar 2022 20:39:59 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: enable threaded NAPI
+
+This can improve performance under load by ensuring that NAPI processing is
+not pinned on CPU 0.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -5036,6 +5036,8 @@ static int mtk_probe(struct platform_dev
+        * for NAPI to work
+        */
+       init_dummy_netdev(&eth->dummy_dev);
++      eth->dummy_dev.threaded = 1;
++      strcpy(eth->dummy_dev.name, "mtk_eth");
+       netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx);
+       netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx);
diff --git a/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch
new file mode 100644 (file)
index 0000000..a556a9c
--- /dev/null
@@ -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
+@@ -1910,6 +1910,9 @@ void phy_detach(struct phy_device *phyde
+       if (phydev->devlink)
+               device_link_del(phydev->devlink);
++      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
+@@ -980,6 +980,12 @@ struct phy_driver {
+       /** @handle_interrupt: Override default interrupt handling */
+       irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
++      /*
++       * Called before an ethernet device is detached
++       * from the PHY.
++       */
++      void (*detach)(struct phy_device *phydev);
++
+       /** @remove: Clears up any memory if needed */
+       void (*remove)(struct phy_device *phydev);
diff --git a/target/linux/generic/pending-6.6/704-netfilter-nf_tables-fix-bidirectional-offload-regres.patch b/target/linux/generic/pending-6.6/704-netfilter-nf_tables-fix-bidirectional-offload-regres.patch
new file mode 100644 (file)
index 0000000..d1d6fa3
--- /dev/null
@@ -0,0 +1,24 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 14 Feb 2024 15:24:41 +0100
+Subject: [PATCH] netfilter: nf_tables: fix bidirectional offload regression
+
+Commit 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
+made unidirectional flow offload possible, while completely ignoring (and
+breaking) bidirectional flow offload for nftables.
+Add the missing flag that was left out as an exercise for the reader :)
+
+Cc: Vlad Buslov <vladbu@nvidia.com>
+Fixes: 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/netfilter/nft_flow_offload.c
++++ b/net/netfilter/nft_flow_offload.c
+@@ -361,6 +361,7 @@ static void nft_flow_offload_eval(const
+               ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
+       }
++      __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
+       ret = flow_offload_add(flowtable, flow);
+       if (ret < 0)
+               goto err_flow_add;
diff --git a/target/linux/generic/pending-6.6/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch b/target/linux/generic/pending-6.6/705-net-dsa-tag_mtk-add-padding-for-tx-packets.patch
new file mode 100644 (file)
index 0000000..f7e4e77
--- /dev/null
@@ -0,0 +1,28 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 6 May 2022 21:38:42 +0200
+Subject: [PATCH] net: dsa: tag_mtk: add padding for tx packets
+
+Padding for transmitted packets needs to account for the special tag.
+With not enough padding, garbage bytes are inserted by the switch at the
+end of small packets.
+
+Fixes: 5cd8985a1909 ("net-next: dsa: add Mediatek tag RX/TX handler")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/dsa/tag_mtk.c
++++ b/net/dsa/tag_mtk.c
+@@ -29,6 +29,13 @@ static struct sk_buff *mtk_tag_xmit(stru
+       skb_set_queue_mapping(skb, dp->index);
++      /* The Ethernet switch we are interfaced with needs packets to be at
++       * least 64 bytes (including FCS) otherwise their padding might be
++       * corrupted. With tags enabled, we need to make sure that packets are
++       * at least 68 bytes (including FCS and tag).
++       */
++      eth_skb_pad(skb);
++
+       /* Build the special tag after the MAC Source Address. If VLAN header
+        * is present, it's required that VLAN header and special tag is
+        * being combined. Only in this way we can allow the switch can parse
diff --git a/target/linux/generic/pending-6.6/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch b/target/linux/generic/pending-6.6/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch
new file mode 100644 (file)
index 0000000..78e0049
--- /dev/null
@@ -0,0 +1,57 @@
+From 4e432e530db0056450fbc4a3cee793f16adc39a7 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 8 Oct 2024 23:58:41 +0100
+Subject: [PATCH] net: phy: populate host_interfaces when attaching PHY
+
+Use bitmask of interfaces supported by the MAC for the PHY to choose
+from if the declared interface mode is among those using a single pair
+of SerDes lanes.
+This will allow 2500Base-T PHYs to switch to SGMII on most hosts, which
+results in half-duplex being supported in case the MAC supports them.
+Without this change, 2500Base-T PHYs will always operate in 2500Base-X
+mode with rate-matching, which is not only wasteful in terms of energy
+consumption, but also limits the supported interface modes to
+full-duplex only.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/phylink.c | 21 ++++++++++++++++++++-
+ 1 file changed, 20 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -2017,7 +2017,7 @@ int phylink_fwnode_phy_connect(struct ph
+ {
+       struct fwnode_handle *phy_fwnode;
+       struct phy_device *phy_dev;
+-      int ret;
++      int i, ret;
+       /* Fixed links and 802.3z are handled without needing a PHY */
+       if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
+@@ -2044,6 +2044,25 @@ int phylink_fwnode_phy_connect(struct ph
+               pl->link_config.interface = pl->link_interface;
+       }
++      /* Assume single-lane SerDes interface modes share the same
++       * lanes and allow the PHY to switch to slower also supported modes
++       */
++      for (i = ARRAY_SIZE(phylink_sfp_interface_preference) - 1; i >= 0; i--) {
++              /* skip unsupported modes */
++              if (!test_bit(phylink_sfp_interface_preference[i], pl->config->supported_interfaces))
++                      continue;
++
++              __set_bit(phylink_sfp_interface_preference[i], phy_dev->host_interfaces);
++
++              /* skip all faster modes */
++              if (phylink_sfp_interface_preference[i] == pl->link_interface)
++                      break;
++      }
++
++      if (test_bit(pl->link_interface, phylink_sfp_interfaces))
++              phy_interface_and(phy_dev->host_interfaces, phylink_sfp_interfaces,
++                                pl->config->supported_interfaces);
++
+       ret = phy_attach_direct(pl->netdev, phy_dev, flags,
+                               pl->link_interface);
+       phy_device_free(phy_dev);
diff --git a/target/linux/generic/pending-6.6/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch b/target/linux/generic/pending-6.6/710-bridge-add-knob-for-filtering-rx-tx-BPDU-pack.patch
new file mode 100644 (file)
index 0000000..96e1a19
--- /dev/null
@@ -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
+@@ -61,6 +61,7 @@ struct br_ip_list {
+ #define BR_PORT_LOCKED                BIT(21)
+ #define BR_PORT_MAB           BIT(22)
+ #define BR_NEIGH_VLAN_SUPPRESS        BIT(23)
++#define BR_BPDU_FILTER                BIT(24)
+ #define BR_DEFAULT_AGEING_TIME        (300 * HZ)
+--- a/net/bridge/br_forward.c
++++ b/net/bridge/br_forward.c
+@@ -201,6 +201,7 @@ void br_flood(struct net_bridge *br, str
+             enum br_pkt_type pkt_type, bool local_rcv, bool local_orig,
+             u16 vid)
+ {
++      const unsigned char *dest = eth_hdr(skb)->h_dest;
+       struct net_bridge_port *prev = NULL;
+       struct net_bridge_port *p;
+@@ -218,6 +219,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
+@@ -367,6 +367,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
+@@ -571,6 +571,7 @@ enum {
+       IFLA_BRPORT_MCAST_MAX_GROUPS,
+       IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
+       IFLA_BRPORT_BACKUP_NHID,
++      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
+@@ -190,6 +190,7 @@ static inline size_t br_port_info_size(v
+               + nla_total_size(1)     /* IFLA_BRPORT_LOCKED */
+               + nla_total_size(1)     /* IFLA_BRPORT_MAB */
+               + nla_total_size(1)     /* IFLA_BRPORT_NEIGH_VLAN_SUPPRESS */
++              + 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 */
+@@ -282,7 +283,8 @@ static int br_port_fill_attrs(struct sk_
+           nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED)) ||
+           nla_put_u8(skb, IFLA_BRPORT_MAB, !!(p->flags & BR_PORT_MAB)) ||
+           nla_put_u8(skb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
+-                     !!(p->flags & BR_NEIGH_VLAN_SUPPRESS)))
++                     !!(p->flags & BR_NEIGH_VLAN_SUPPRESS)) ||
++          nla_put_u8(skb, IFLA_BRPORT_BPDU_FILTER, !!(p->flags & BR_BPDU_FILTER)))
+               return -EMSGSIZE;
+       timerval = br_timer_value(&p->message_age_timer);
+@@ -902,6 +904,7 @@ static const struct nla_policy br_port_p
+       [IFLA_BRPORT_MCAST_MAX_GROUPS] = { .type = NLA_U32 },
+       [IFLA_BRPORT_NEIGH_VLAN_SUPPRESS] = NLA_POLICY_MAX(NLA_U8, 1),
+       [IFLA_BRPORT_BACKUP_NHID] = { .type = NLA_U32 },
++      [IFLA_BRPORT_BPDU_FILTER] = { .type = NLA_U8 },
+ };
+ /* Change the state of the port and notify spanning tree */
+@@ -970,6 +973,7 @@ static int br_setport(struct net_bridge_
+       br_set_port_flag(p, tb, IFLA_BRPORT_MAB, BR_PORT_MAB);
+       br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS,
+                        BR_NEIGH_VLAN_SUPPRESS);
++      br_set_port_flag(p, tb, IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER);
+       if ((p->flags & BR_PORT_MAB) &&
+           (!(p->flags & BR_PORT_LOCKED) || !(p->flags & BR_LEARNING))) {
+--- a/net/core/rtnetlink.c
++++ b/net/core/rtnetlink.c
+@@ -61,7 +61,7 @@
+ #include "dev.h"
+ #define RTNL_MAX_TYPE         50
+-#define RTNL_SLAVE_MAX_TYPE   44
++#define RTNL_SLAVE_MAX_TYPE   45
+ struct rtnl_link {
+       rtnl_doit_func          doit;
+@@ -4978,7 +4978,9 @@ int ndo_dflt_bridge_getlink(struct sk_bu
+           brport_nla_put_flag(skb, flags, mask,
+                               IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD) ||
+           brport_nla_put_flag(skb, flags, mask,
+-                              IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD)) {
++                              IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD) ||
++          brport_nla_put_flag(skb, flags, mask,
++                              IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER)) {
+               nla_nest_cancel(skb, protinfo);
+               goto nla_put_failure;
+       }
diff --git a/target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch b/target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch
new file mode 100644 (file)
index 0000000..3197aea
--- /dev/null
@@ -0,0 +1,86 @@
+From 3b4329230db8750bea7a56ef07f07cbbf5fc6c5a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 4 Jul 2023 22:50:12 +0200
+Subject: [PATCH 19/20] net: dsa: qca8k: implement lag_fdb_add/del ops
+
+Implement lag_fdb_add/del ops to correctly support using LAG interface.
+Qca8k switch supports declaring fdb entry for link aggregation by simply
+setting the DES_PORT bits to all the LAG member.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c   |  2 ++
+ drivers/net/dsa/qca/qca8k-common.c | 48 ++++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h        |  6 ++++
+ 3 files changed, 56 insertions(+)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -2012,6 +2012,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,
++      .lag_fdb_add            = qca8k_lag_fdb_add,
++      .lag_fdb_del            = qca8k_lag_fdb_del,
+       .port_mdb_add           = qca8k_port_mdb_add,
+       .port_mdb_del           = qca8k_port_mdb_del,
+       .port_mirror_add        = qca8k_port_mirror_add,
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -1215,6 +1215,42 @@ int qca8k_port_lag_leave(struct dsa_swit
+       return qca8k_lag_refresh_portmap(ds, port, lag, true);
+ }
++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag,
++                    const unsigned char *addr, u16 vid,
++                    struct dsa_db db)
++{
++      struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++      struct dsa_port *dp;
++      u16 port_mask = 0;
++
++      /* Set the vid to the port vlan id if no vid is set */
++      if (!vid)
++              vid = QCA8K_PORT_VID_DEF;
++
++      dsa_lag_foreach_port(dp, ds->dst, &lag)
++              port_mask |= BIT(dp->index);
++
++      return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
++}
++
++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag,
++                    const unsigned char *addr, u16 vid,
++                    struct dsa_db db)
++{
++      struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
++      struct dsa_port *dp;
++      u16 port_mask = 0;
++
++      /* Set the vid to the port vlan id if no vid is set */
++      if (!vid)
++              vid = QCA8K_PORT_VID_DEF;
++
++      dsa_lag_foreach_port(dp, ds->dst, &lag)
++      port_mask |= BIT(dp->index);
++
++      return qca8k_fdb_del(priv, addr, port_mask, vid);
++}
++
+ int qca8k_read_switch_id(struct qca8k_priv *priv)
+ {
+       u32 val;
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -590,5 +590,11 @@ int qca8k_port_lag_join(struct dsa_switc
+                       struct netlink_ext_ack *extack);
+ int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
+                        struct dsa_lag lag);
++int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag,
++                    const unsigned char *addr, u16 vid,
++                    struct dsa_db db);
++int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag,
++                    const unsigned char *addr, u16 vid,
++                    struct dsa_db db);
+ #endif /* __QCA8K_H */
diff --git a/target/linux/generic/pending-6.6/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch b/target/linux/generic/pending-6.6/711-02-net-dsa-qca8k-enable-flooding-to-both-CPU-port.patch
new file mode 100644 (file)
index 0000000..b1d9f84
--- /dev/null
@@ -0,0 +1,37 @@
+From b954d61d9ecfa64450fc178586719dc2a95b92a7 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 20 Jun 2023 21:48:24 +0200
+Subject: [PATCH 3/4] net: dsa: qca8k: enable flooding to both CPU port
+
+To permit a multi-CPU setup, flood all unknown frames to all CPU ports.
+Each CPU port should have correct LOOKUP MEMBER configuration to
+prevent receiving duplicate packets from user ports.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 13 +++++--------
+ 1 file changed, 5 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1901,15 +1901,12 @@ qca8k_setup(struct dsa_switch *ds)
+               }
+       }
+-      /* 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
+-       */
++      /* Forward all unknown frames to CPU port for Linux processing */
+       ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
+-                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) |
+-                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) |
+-                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) |
+-                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));
++                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, dsa_cpu_ports(ds)) |
++                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, dsa_cpu_ports(ds)) |
++                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, dsa_cpu_ports(ds)) |
++                        FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, dsa_cpu_ports(ds)));
+       if (ret)
+               return ret;
diff --git a/target/linux/generic/pending-6.6/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch b/target/linux/generic/pending-6.6/711-03-net-dsa-qca8k-add-support-for-port_change_master.patch
new file mode 100644 (file)
index 0000000..9b553e7
--- /dev/null
@@ -0,0 +1,158 @@
+From b2d6ebf2f92f8695c83fa6979f4ab579c588df76 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Tue, 20 Jun 2023 07:57:38 +0200
+Subject: [PATCH 4/4] net: dsa: qca8k: add support for port_change_master
+
+Add support for port_change_master to permit assigning an alternative
+CPU port if the switch have both CPU port connected or create a LAG on
+both CPU port and assign the LAG as DSA master.
+
+On port change master request, we check if the master is a LAG.
+With LAG we compose the cpu_port_mask with the CPU port in the LAG, if
+master is a simple dsa_port, we derive the index.
+
+Finally we apply the new cpu_port_mask to the LOOKUP MEMBER to permit
+the port to receive packet by the new CPU port setup for the port and we
+refresh the CPU ports LOOKUP MEMBER configuration to reflect the new
+user port state.
+
+port_lag_join/leave is updated to refresh the user ports if we detect
+that the LAG is a DSA master and we have user port using it as a master.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 116 ++++++++++++++++++++++++++++++-
+ 1 file changed, 114 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1738,6 +1738,117 @@ qca8k_get_tag_protocol(struct dsa_switch
+       return DSA_TAG_PROTO_QCA;
+ }
++static int qca8k_port_change_master(struct dsa_switch *ds, int port,
++                                  struct net_device *master,
++                                  struct netlink_ext_ack *extack)
++{
++      struct dsa_switch_tree *dst = ds->dst;
++      struct qca8k_priv *priv = ds->priv;
++      u8 cpu_port_mask = 0;
++      struct dsa_port *dp;
++      u32 val;
++      int ret;
++
++      /* With LAG of CPU port, compose the mask for port LOOKUP MEMBER */
++      if (netif_is_lag_master(master)) {
++              struct dsa_lag *lag;
++              int id;
++
++              id = dsa_lag_id(dst, master);
++              lag = dsa_lag_by_id(dst, id);
++
++              dsa_lag_foreach_port(dp, dst, lag)
++                      if (dsa_port_is_cpu(dp))
++                              cpu_port_mask |= BIT(dp->index);
++      } else {
++              dp = master->dsa_ptr;
++              cpu_port_mask |= BIT(dp->index);
++      }
++
++      /* Connect port to new cpu port */
++      ret = regmap_read(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), &val);
++      if (ret)
++              return ret;
++
++      /* Reset connected CPU port in port LOOKUP MEMBER */
++      val &=  ~dsa_cpu_ports(ds);
++      /* Assign the new CPU port in port LOOKUP MEMBER */
++      val |= cpu_port_mask;
++
++      ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
++                               QCA8K_PORT_LOOKUP_MEMBER,
++                               val);
++      if (ret)
++              return ret;
++
++      /* Refresh CPU port LOOKUP MEMBER with new port */
++      dsa_tree_for_each_cpu_port(dp, ds->dst) {
++              u32 reg = QCA8K_PORT_LOOKUP_CTRL(dp->index);
++
++              /* If CPU port in mask assign port, else remove port */
++              if (BIT(dp->index) & cpu_port_mask)
++                      ret = regmap_set_bits(priv->regmap, reg, BIT(port));
++              else
++                      ret = regmap_clear_bits(priv->regmap, reg, BIT(port));
++
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static int qca8k_port_lag_refresh_user_ports(struct dsa_switch *ds,
++                                           struct dsa_lag lag)
++{
++      struct net_device *lag_dev = lag.dev;
++      struct dsa_port *dp;
++      int ret;
++
++      /* Ignore if LAG is not a DSA master */
++      if (!netif_is_lag_master(lag_dev))
++              return 0;
++
++      dsa_switch_for_each_user_port(dp, ds) {
++              /* Skip if assigned master is not the LAG */
++              if (dsa_port_to_master(dp) != lag_dev)
++                      continue;
++
++              ret = qca8k_port_change_master(ds, dp->index,
++                                             lag_dev, NULL);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++static int qca8xxx_port_lag_join(struct dsa_switch *ds, int port,
++                               struct dsa_lag lag,
++                               struct netdev_lag_upper_info *info,
++                               struct netlink_ext_ack *extack)
++{
++      int ret;
++
++      ret = qca8k_port_lag_join(ds, port, lag, info, extack);
++      if (ret)
++              return ret;
++
++      return qca8k_port_lag_refresh_user_ports(ds, lag);
++}
++
++static int qca8xxx_port_lag_leave(struct dsa_switch *ds, int port,
++                                struct dsa_lag lag)
++{
++      int ret;
++
++      ret = qca8k_port_lag_leave(ds, port, lag);
++      if (ret)
++              return ret;
++
++      return qca8k_port_lag_refresh_user_ports(ds, lag);
++}
++
+ static void
+ qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
+                   bool operational)
+@@ -2024,8 +2135,9 @@ 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,
++      .port_lag_join          = qca8xxx_port_lag_join,
++      .port_lag_leave         = qca8xxx_port_lag_leave,
++      .port_change_master     = qca8k_port_change_master,
+       .master_state_change    = qca8k_master_change,
+       .connect_tag_protocol   = qca8k_connect_tag_protocol,
+ };
diff --git a/target/linux/generic/pending-6.6/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-6.6/712-net-dsa-qca8k-enable-assisted-learning-on-CPU-port.patch
new file mode 100644 (file)
index 0000000..18afa1c
--- /dev/null
@@ -0,0 +1,57 @@
+From 0f6599167c126ce32c85d4f8a1f3d1775a268572 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Fri, 6 Oct 2023 12:44:00 +0200
+Subject: [PATCH] net: dsa: qca8k: enable assisted learning on CPU port
+
+Enable assisted learning on CPU port.
+
+It has been verified that there is a problem in packet roaming
+from one BSS to another in the same security settings from one
+physical R7800 to another physical R7800 where they are in the
+same L2 broadcast domain backhauled/linked together via one
+of the ethernet ports.
+DHCP will fail to complete and traffic cannot flow for around 300
+seconds.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -2010,6 +2010,12 @@ qca8k_setup(struct dsa_switch *ds)
+                       dev_err(priv->dev, "failed enabling QCA header mode on port %d", dp->index);
+                       return ret;
+               }
++
++              /* Disable learning by default on all ports */
++              ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(dp->index),
++                                                              QCA8K_PORT_LOOKUP_LEARN);
++              if (ret)
++                      return ret;
+       }
+       /* Forward all unknown frames to CPU port for Linux processing */
+@@ -2039,11 +2045,6 @@ qca8k_setup(struct dsa_switch *ds)
+               if (ret)
+                       return ret;
+-              ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+-                                      QCA8K_PORT_LOOKUP_LEARN);
+-              if (ret)
+-                      return ret;
+-
+               /* For port based vlans to work we need to set the
+                * default egress vid
+                */
+@@ -2095,6 +2096,9 @@ qca8k_setup(struct dsa_switch *ds)
+       /* Set max number of LAGs supported */
+       ds->num_lag_ids = QCA8K_NUM_LAGS;
++      /* HW learn on CPU port is limited and require manual setting */
++      ds->assisted_learning_on_cpu_port = true;
++
+       return 0;
+ }
diff --git a/target/linux/generic/pending-6.6/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch b/target/linux/generic/pending-6.6/713-03-arm64-dts-qcom-ipq8074-add-clock-frequency-to-MDIO-n.patch
new file mode 100644 (file)
index 0000000..74cce98
--- /dev/null
@@ -0,0 +1,25 @@
+From 3b5a603bf66236b956287909556fd7ad4904450c Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 24 Jan 2024 19:38:01 +0100
+Subject: [PATCH 3/3] arm64: dts: qcom: ipq8074: add clock-frequency to MDIO
+ node
+
+Add clock-frequency to MDIO node to set the MDC rate to 6.25Mhz instead
+of using the default value of 390KHz from MDIO default divider.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ arch/arm64/boot/dts/qcom/ipq8074.dtsi | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi
+@@ -275,6 +275,8 @@
+                       clocks = <&gcc GCC_MDIO_AHB_CLK>;
+                       clock-names = "gcc_mdio_ahb_clk";
++                      clock-frequency = <6250000>;
++
+                       status = "disabled";
+               };
diff --git a/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
new file mode 100644 (file)
index 0000000..2b77b6e
--- /dev/null
@@ -0,0 +1,81 @@
+From 85cd45580f5e3b26068cccb7d6173f200e754dc0 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 2 Apr 2023 23:56:16 +0100
+Subject: [PATCH 1/2] net: phy: realtek: use genphy_soft_reset for 2.5G PHYs
+
+Some vendor bootloaders do weird things with those PHYs which result in
+link modes being reported wrongly. Start from a clean sheet by resetting
+the PHY.
+
+Reported-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1375,6 +1375,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .name           = "RTL8226 2.5Gbps PHY",
+               .match_phy_device = rtl8226_match_phy_device,
++              .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .read_status    = rtl822x_read_status,
+@@ -1387,6 +1388,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               PHY_ID_MATCH_EXACT(0x001cc840),
+               .name           = "RTL8226B_RTL8221B 2.5Gbps PHY",
++              .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
+@@ -1401,6 +1403,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               PHY_ID_MATCH_EXACT(0x001cc838),
+               .name           = "RTL8226-CG 2.5Gbps PHY",
++              .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .read_status    = rtl822x_read_status,
+@@ -1411,6 +1414,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               PHY_ID_MATCH_EXACT(0x001cc848),
+               .name           = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
++              .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
+@@ -1423,6 +1427,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
+               .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
++              .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
+@@ -1435,6 +1440,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
+               .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
++              .soft_reset     = genphy_soft_reset,
+               .config_init    = rtl822xb_config_init,
+               .get_rate_matching = rtl822xb_get_rate_matching,
+               .get_features   = rtl822x_c45_get_features,
+@@ -1445,6 +1451,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
+               .name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
++              .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+               .config_init    = rtl822xb_config_init,
+@@ -1457,6 +1464,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
+               .name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
++              .soft_reset     = genphy_soft_reset,
+               .config_init    = rtl822xb_config_init,
+               .get_rate_matching = rtl822xb_get_rate_matching,
+               .get_features   = rtl822x_c45_get_features,
diff --git a/target/linux/generic/pending-6.6/720-02-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch b/target/linux/generic/pending-6.6/720-02-net-phy-realtek-disable-SGMII-in-band-AN-for-2-5G-PHYs.patch
new file mode 100644 (file)
index 0000000..cbef74d
--- /dev/null
@@ -0,0 +1,63 @@
+From d54ef6aea00e7a6ace439baade6ad0aa38ee4b04 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 3 Apr 2023 01:21:57 +0300
+Subject: [PATCH 287/326] net: phy: realtek: disable SGMII in-band AN for 2.5G
+ PHYs
+
+MAC drivers don't use SGMII in-band autonegotiation unless told to do so
+in device tree using 'managed = "in-band-status"'. When using MDIO to
+access a PHY, in-band-status is unneeded as we have link-status via
+MDIO. Switch off SGMII in-band autonegotiation using magic values.
+
+Reported-by: Chen Minqiang <ptpt52@gmail.com>
+Reported-by: Chukun Pan <amadeus@jmu.edu.cn>
+Reported-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
+Tested-by: Yevhen Kolomeiko <jarvis2709@gmail.com>
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 27 +++++++++++++++++++++++++--
+ 1 file changed, 25 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -814,8 +814,8 @@ static int rtl822x_write_mmd(struct phy_
+ static int rtl822xb_config_init(struct phy_device *phydev)
+ {
+       bool has_2500, has_sgmii;
++      int ret, val;
+       u16 mode;
+-      int ret;
+       has_2500 = test_bit(PHY_INTERFACE_MODE_2500BASEX,
+                           phydev->host_interfaces) ||
+@@ -865,7 +865,29 @@ static int rtl822xb_config_init(struct p
+       if (ret < 0)
+               return ret;
+-      return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
++      if (ret < 0)
++              return ret;
++
++      /* Disable SGMII AN */
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7588, 0x2);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7589, 0x71d0);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x7587, 0x3);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, 0x7587,
++                                      val, !(val & BIT(0)), 500, 100000, false);
++      if (ret < 0)
++              return ret;
++
++      return 0;
+ }
+ static int rtl822xb_get_rate_matching(struct phy_device *phydev,
diff --git a/target/linux/generic/pending-6.6/720-03-net-phy-realtek-make-sure-paged-read-is-protected-by.patch b/target/linux/generic/pending-6.6/720-03-net-phy-realtek-make-sure-paged-read-is-protected-by.patch
new file mode 100644 (file)
index 0000000..216fa91
--- /dev/null
@@ -0,0 +1,35 @@
+From 4dd2cc9b91ecb25f278a2c55e07e6455e9000e6b Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 22 Apr 2023 01:21:14 +0100
+Subject: [PATCH] net: phy: realtek: make sure paged read is protected by mutex
+
+As we cannot rely on phy_read_paged function before the PHY is
+identified, the paged read in rtlgen_supports_2_5gbps needs to be open
+coded as it is being called by the match_phy_device function, ie. before
+.read_page and .write_page have been populated.
+
+Make sure it is also protected by the MDIO bus mutex and use
+rtl821x_write_page instead of 3 individually locked MDIO bus operations.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1092,9 +1092,11 @@ static bool rtlgen_supports_2_5gbps(stru
+ {
+       int val;
+-      phy_write(phydev, RTL821x_PAGE_SELECT, 0xa61);
+-      val = phy_read(phydev, 0x13);
+-      phy_write(phydev, RTL821x_PAGE_SELECT, 0);
++      mutex_lock(&phydev->mdio.bus->mdio_lock);
++      rtl821x_write_page(phydev, 0xa61);
++      val = __phy_read(phydev, 0x13);
++      rtl821x_write_page(phydev, 0);
++      mutex_unlock(&phydev->mdio.bus->mdio_lock);
+       return val >= 0 && val & MDIO_PMA_SPEED_2_5G;
+ }
diff --git a/target/linux/generic/pending-6.6/720-04-net-phy-realtek-introduce-rtl822x_probe.patch b/target/linux/generic/pending-6.6/720-04-net-phy-realtek-introduce-rtl822x_probe.patch
new file mode 100644 (file)
index 0000000..08c13f3
--- /dev/null
@@ -0,0 +1,100 @@
+From 9155098547fb1172d4fa536f3f6bc9d42f59d08c Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sat, 22 Apr 2023 03:26:01 +0100
+Subject: [PATCH] net: phy: realtek: setup ALDPS on RTL822x
+
+Setup Link Down Power Saving Mode according the DTS property
+just like for RTL821x 1GE PHYs.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/phy/realtek.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -80,6 +80,10 @@
+ #define RTL822X_VND2_GANLPAR                          0xa414
++#define RTL8221B_PHYCR1                               0xa430
++#define RTL8221B_PHYCR1_ALDPS_EN              BIT(2)
++#define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN     BIT(12)
++
+ #define RTL8366RB_POWER_SAVE                  0x15
+ #define RTL8366RB_POWER_SAVE_ON                       BIT(12)
+@@ -1152,6 +1156,25 @@ static int rtl8251b_c45_match_phy_device
+       return rtlgen_is_c45_match(phydev, RTL_8251B, true);
+ }
++static int rtl822x_probe(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      int val;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL8221B_PHYCR1);
++      if (val < 0)
++              return val;
++
++      if (of_property_read_bool(dev->of_node, "realtek,aldps-enable"))
++              val |= RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN;
++      else
++              val &= ~(RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN);
++
++      phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL8221B_PHYCR1, val);
++
++      return 0;
++}
++
+ static int rtlgen_resume(struct phy_device *phydev)
+ {
+       int ret = genphy_resume(phydev);
+@@ -1427,6 +1450,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               PHY_ID_MATCH_EXACT(0x001cc838),
+               .name           = "RTL8226-CG 2.5Gbps PHY",
++              .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+@@ -1438,6 +1462,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               PHY_ID_MATCH_EXACT(0x001cc848),
+               .name           = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
++              .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+@@ -1451,6 +1476,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
+               .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
++              .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+@@ -1464,6 +1490,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
+               .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
++              .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .config_init    = rtl822xb_config_init,
+               .get_rate_matching = rtl822xb_get_rate_matching,
+@@ -1475,6 +1502,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
+               .name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
++              .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+               .config_aneg    = rtl822x_config_aneg,
+@@ -1488,6 +1516,7 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
+               .name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
++              .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .config_init    = rtl822xb_config_init,
+               .get_rate_matching = rtl822xb_get_rate_matching,
diff --git a/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
new file mode 100644 (file)
index 0000000..6938552
--- /dev/null
@@ -0,0 +1,52 @@
+From 0de82310d2b32e78ff79d42c08b1122a6ede3778 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 30 Apr 2023 00:15:41 +0100
+Subject: [PATCH] net: phy: realtek: detect early version of RTL8221B
+
+Early versions (?) of the RTL8221B PHY cannot be identified in a regular
+Clause-45 bus scan as the PHY doesn't report the implemented MMDs
+correctly but returns 0 instead.
+Implement custom identify function using the PKGID instead of iterating
+over the implemented MMDs.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+[forward-port by @namiltd]
+Signed-off-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1120,10 +1120,32 @@ static int rtl8226_match_phy_device(stru
+ static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
+                              bool is_c45)
+ {
+-      if (phydev->is_c45)
+-              return is_c45 && (id == phydev->c45_ids.device_ids[1]);
+-      else
++      if (phydev->is_c45) {
++              u32 rid;
++
++              if (!is_c45)
++                      return 0;
++
++              rid = phydev->c45_ids.device_ids[1];
++              if ((rid == 0xffffffff) && phydev->mdio.bus->read_c45) {
++                      int val;
++
++                      val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID1);
++                      if (val < 0)
++                              return 0;
++
++                      rid = val << 16;
++                      val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID2);
++                      if (val < 0)
++                              return 0;
++
++                      rid |= val;
++              }
++
++              return (id == rid);
++      } else {
+               return !is_c45 && (id == phydev->phy_id);
++      }
+ }
+ static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
diff --git a/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch b/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch
new file mode 100644 (file)
index 0000000..a796b8f
--- /dev/null
@@ -0,0 +1,102 @@
+From d7943c31d57c11e1a517aa3ce2006fca44866870 Mon Sep 17 00:00:00 2001
+From: Jianhui Zhao <zhaojh329@gmail.com>
+Date: Sun, 24 Sep 2023 22:15:00 +0800
+Subject: [PATCH] net: phy: realtek: add interrupt support for RTL8221B
+
+This commit introduces interrupt support for RTL8221B.
+
+Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
+---
+ drivers/net/phy/realtek.c | 47 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1332,6 +1332,51 @@ static irqreturn_t rtl9000a_handle_inter
+       return IRQ_HANDLED;
+ }
++static int rtl8221b_ack_interrupt(struct phy_device *phydev)
++{
++      int err;
++
++      err = phy_read_mmd(phydev, MDIO_MMD_VEND2, 0xa4d4);
++
++      return (err < 0) ? err : 0;
++}
++
++static int rtl8221b_config_intr(struct phy_device *phydev)
++{
++      int err;
++
++      if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
++              err = rtl8221b_ack_interrupt(phydev);
++              if (err)
++                      return err;
++
++              err = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xa4d2, 0x7ff);
++      } else {
++              err = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xa4d2, 0x0);
++              if (err)
++                      return err;
++
++              err = rtl8221b_ack_interrupt(phydev);
++      }
++
++      return err;
++}
++
++static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev)
++{
++      int err;
++
++      err = rtl8221b_ack_interrupt(phydev);
++      if (err) {
++              phy_error(phydev);
++              return IRQ_NONE;
++      }
++
++      phy_trigger_machine(phydev);
++
++      return IRQ_HANDLED;
++}
++
+ static struct phy_driver realtek_drvs[] = {
+       {
+               PHY_ID_MATCH_EXACT(0x00008201),
+@@ -1498,6 +1543,8 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
+               .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
++              .config_intr    = rtl8221b_config_intr,
++              .handle_interrupt = rtl8221b_handle_interrupt,
+               .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+@@ -1512,6 +1559,8 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
+               .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
++              .config_intr    = rtl8221b_config_intr,
++              .handle_interrupt = rtl8221b_handle_interrupt,
+               .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .config_init    = rtl822xb_config_init,
+@@ -1524,6 +1573,8 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
+               .name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
++              .config_intr    = rtl8221b_config_intr,
++              .handle_interrupt = rtl8221b_handle_interrupt,
+               .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .get_features   = rtl822x_get_features,
+@@ -1538,6 +1589,8 @@ static struct phy_driver realtek_drvs[]
+       }, {
+               .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
+               .name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
++              .config_intr    = rtl8221b_config_intr,
++              .handle_interrupt = rtl8221b_handle_interrupt,
+               .probe          = rtl822x_probe,
+               .soft_reset     = genphy_soft_reset,
+               .config_init    = rtl822xb_config_init,
diff --git a/target/linux/generic/pending-6.6/730-net-ethernet-mtk_eth_soc-reset-all-TX-queues-on-DMA-.patch b/target/linux/generic/pending-6.6/730-net-ethernet-mtk_eth_soc-reset-all-TX-queues-on-DMA-.patch
new file mode 100644 (file)
index 0000000..67d0ab4
--- /dev/null
@@ -0,0 +1,49 @@
+From 7d41a5a8e9c91cc6bb011dd953570738583dd091 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 18 Sep 2024 02:01:01 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: reset all TX queues on DMA free
+
+The purpose of resetting the TX queue is to reset the
+byte and packet count as well as to clear the software
+flow control XOFF bit.
+
+MediaTek developers pointed out that netdev_reset_queue would only
+resets queue 0 of the network device.
+Queues that are not reset may cause unexpected issues.
+
+Packets may stop being sent after reset and "transmit timeout" log may
+be displayed.
+
+Import fix from MediaTek's SDK to resolve this issue.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c | 18 ++++++++++++++----
+ 1 file changed, 14 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3135,11 +3135,19 @@ static int mtk_dma_init(struct mtk_eth *
+ static void mtk_dma_free(struct mtk_eth *eth)
+ {
+       const struct mtk_soc_data *soc = eth->soc;
+-      int i;
++      int i, j, txqs = 1;
++
++      if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA))
++              txqs = MTK_QDMA_NUM_QUEUES;
++
++      for (i = 0; i < MTK_MAX_DEVS; i++) {
++              if (!eth->netdev[i])
++                      continue;
++
++              for (j = 0; j < txqs; j++)
++                      netdev_tx_reset_queue(netdev_get_tx_queue(eth->netdev[i], j));
++      }
+-      for (i = 0; i < MTK_MAX_DEVS; i++)
+-              if (eth->netdev[i])
+-                      netdev_reset_queue(eth->netdev[i]);
+       if (!MTK_HAS_CAPS(soc->caps, MTK_SRAM) && eth->scratch_ring) {
+               dma_free_coherent(eth->dma_dev,
+                                 MTK_QDMA_RING_SIZE * soc->tx.desc_size,
diff --git a/target/linux/generic/pending-6.6/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch b/target/linux/generic/pending-6.6/731-net-permit-ieee80211_ptr-even-with-no-CFG82111-suppo.patch
new file mode 100644 (file)
index 0000000..1f07c0f
--- /dev/null
@@ -0,0 +1,59 @@
+From 686c603f67ae87bf21a61b5e4b1564443f41c3ee Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 20 Oct 2022 03:34:43 +0200
+Subject: [PATCH] net: permit ieee80211_ptr even with no CFG82111 support
+
+Introduce a new flag CONFIG_CFG80211_HEADERS to compile in ieee80211_ptr
+even if CFG80211 support is not compiled in. This is needed for the
+backports project and for any downstream wireless driver that loads in
+the kernel dynamically.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ include/linux/netdevice.h       | 2 +-
+ net/batman-adv/hard-interface.c | 2 +-
+ net/wireless/Kconfig            | 4 ++++
+ 3 files changed, 6 insertions(+), 2 deletions(-)
+
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -2243,7 +2243,7 @@ struct net_device {
+ #if IS_ENABLED(CONFIG_AX25)
+       void                    *ax25_ptr;
+ #endif
+-#if IS_ENABLED(CONFIG_CFG80211)
++#if IS_ENABLED(CONFIG_CFG80211_HEADERS)
+       struct wireless_dev     *ieee80211_ptr;
+ #endif
+ #if IS_ENABLED(CONFIG_IEEE802154) || IS_ENABLED(CONFIG_6LOWPAN)
+--- a/net/batman-adv/hard-interface.c
++++ b/net/batman-adv/hard-interface.c
+@@ -309,7 +309,7 @@ static bool batadv_is_cfg80211_netdev(st
+       if (!net_device)
+               return false;
+-#if IS_ENABLED(CONFIG_CFG80211)
++#if IS_ENABLED(CONFIG_CFG80211_HEADERS)
+       /* cfg80211 drivers have to set ieee80211_ptr */
+       if (net_device->ieee80211_ptr)
+               return true;
+--- a/net/wireless/Kconfig
++++ b/net/wireless/Kconfig
+@@ -26,6 +26,7 @@ config CFG80211
+       # using a different algorithm, though right now they shouldn't
+       # (this is here rather than below to allow it to be a module)
+       select CRYPTO_SHA256 if CFG80211_USE_KERNEL_REGDB_KEYS
++      select CFG80211_HEADERS
+       help
+         cfg80211 is the Linux wireless LAN (802.11) configuration API.
+         Enable this if you have a wireless device.
+@@ -36,6 +37,9 @@ config CFG80211
+         When built as a module it will be called cfg80211.
++config CFG80211_HEADERS
++      bool "cfg80211 - headers support"
++
+ if CFG80211
+ config NL80211_TESTMODE
diff --git a/target/linux/generic/pending-6.6/732-00-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch b/target/linux/generic/pending-6.6/732-00-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch
new file mode 100644 (file)
index 0000000..c3297a1
--- /dev/null
@@ -0,0 +1,44 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 27 Oct 2022 23:39:52 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: compile out netsys v2 code
+ on mt7621
+
+Avoid some branches in the hot path on low-end devices with limited CPU power,
+and reduce code size
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -1336,6 +1336,22 @@ struct mtk_mac {
+ /* the struct describing the SoC. these are declared in the soc_xyz.c files */
+ extern const struct of_device_id of_mtk_match[];
++#ifdef CONFIG_SOC_MT7621
++static inline bool mtk_is_netsys_v1(struct mtk_eth *eth)
++{
++      return true;
++}
++
++static inline bool mtk_is_netsys_v2_or_greater(struct mtk_eth *eth)
++{
++      return false;
++}
++
++static inline bool mtk_is_netsys_v3_or_greater(struct mtk_eth *eth)
++{
++      return false;
++}
++#else
+ static inline bool mtk_is_netsys_v1(struct mtk_eth *eth)
+ {
+       return eth->soc->version == 1;
+@@ -1350,6 +1366,7 @@ static inline bool mtk_is_netsys_v3_or_g
+ {
+       return eth->soc->version > 2;
+ }
++#endif
+ static inline struct mtk_foe_entry *
+ mtk_foe_get_entry(struct mtk_ppe *ppe, u16 hash)
diff --git a/target/linux/generic/pending-6.6/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch b/target/linux/generic/pending-6.6/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch
new file mode 100644 (file)
index 0000000..5318793
--- /dev/null
@@ -0,0 +1,102 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 3 Nov 2022 12:38:49 +0100
+Subject: [PATCH] net: ethernet: mtk_eth_soc: work around issue with sending
+ small fragments
+
+When lots of frames are sent with a number of very small fragments, an
+internal FIFO can overflow, causing the DMA engine to lock up lock up and
+transmit attempts time out.
+
+Fix this on MT7986 by increasing the reserved FIFO space.
+Fix this on older chips by detecting the presence of small fragments and use
+skb_gso_segment + skb_linearize to deal with them.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -26,6 +26,7 @@
+ #include <linux/bitfield.h>
+ #include <net/dsa.h>
+ #include <net/dst_metadata.h>
++#include <net/gso.h>
+ #include <net/page_pool/helpers.h>
+ #include "mtk_eth_soc.h"
+@@ -1596,12 +1597,28 @@ static void mtk_wake_queue(struct mtk_et
+       }
+ }
++static bool mtk_skb_has_small_frag(struct sk_buff *skb)
++{
++      int min_size = 16;
++      int i;
++
++      if (skb_headlen(skb) < min_size)
++              return true;
++
++      for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
++              if (skb_frag_size(&skb_shinfo(skb)->frags[i]) < min_size)
++                      return true;
++
++      return false;
++}
++
+ static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
+       struct mtk_mac *mac = netdev_priv(dev);
+       struct mtk_eth *eth = mac->hw;
+       struct mtk_tx_ring *ring = &eth->tx_ring;
+       struct net_device_stats *stats = &dev->stats;
++      struct sk_buff *segs, *next;
+       bool gso = false;
+       int tx_num;
+@@ -1623,6 +1640,18 @@ static netdev_tx_t mtk_start_xmit(struct
+               return NETDEV_TX_BUSY;
+       }
++      if (mtk_is_netsys_v1(eth) &&
++          skb_is_gso(skb) && mtk_skb_has_small_frag(skb)) {
++              segs = skb_gso_segment(skb, dev->features & ~NETIF_F_ALL_TSO);
++              if (IS_ERR(segs))
++                      goto drop;
++
++              if (segs) {
++                      consume_skb(skb);
++                      skb = segs;
++              }
++      }
++
+       /* TSO: fill MSS info in tcp checksum field */
+       if (skb_is_gso(skb)) {
+               if (skb_cow_head(skb, 0)) {
+@@ -1638,8 +1667,14 @@ static netdev_tx_t mtk_start_xmit(struct
+               }
+       }
+-      if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
+-              goto drop;
++      skb_list_walk_safe(skb, skb, next) {
++              if ((mtk_is_netsys_v1(eth) &&
++                   mtk_skb_has_small_frag(skb) && skb_linearize(skb)) ||
++                  mtk_tx_map(skb, dev, tx_num, ring, gso) < 0) {
++                              stats->tx_dropped++;
++                              dev_kfree_skb_any(skb);
++              }
++      }
+       if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
+               netif_tx_stop_all_queues(dev);
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -270,7 +270,7 @@
+ #define MTK_CHK_DDONE_EN      BIT(28)
+ #define MTK_DMAD_WR_WDONE     BIT(26)
+ #define MTK_WCOMP_EN          BIT(24)
+-#define MTK_RESV_BUF          (0x40 << 16)
++#define MTK_RESV_BUF          (0x80 << 16)
+ #define MTK_MUTLI_CNT         (0x4 << 12)
+ #define MTK_LEAKY_BUCKET_EN   BIT(11)
diff --git a/target/linux/generic/pending-6.6/732-02-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch b/target/linux/generic/pending-6.6/732-02-net-ethernet-mtk_eth_soc-set-NETIF_F_ALL_TSO.patch
new file mode 100644 (file)
index 0000000..bd7a1b9
--- /dev/null
@@ -0,0 +1,21 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 28 Oct 2022 12:54:48 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: set NETIF_F_ALL_TSO
+
+Significantly improves performance by avoiding unnecessary segmentation
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -49,8 +49,7 @@
+ #define MTK_HW_FEATURES               (NETIF_F_IP_CSUM | \
+                                NETIF_F_RXCSUM | \
+                                NETIF_F_HW_VLAN_CTAG_TX | \
+-                               NETIF_F_SG | NETIF_F_TSO | \
+-                               NETIF_F_TSO6 | \
++                               NETIF_F_SG | NETIF_F_ALL_TSO | \
+                                NETIF_F_IPV6_CSUM |\
+                                NETIF_F_HW_TC)
+ #define MTK_HW_FEATURES_MT7628        (NETIF_F_SG | NETIF_F_RXCSUM)
diff --git a/target/linux/generic/pending-6.6/733-01-net-ethernet-mtk_eth_soc-use-napi_build_skb.patch b/target/linux/generic/pending-6.6/733-01-net-ethernet-mtk_eth_soc-use-napi_build_skb.patch
new file mode 100644 (file)
index 0000000..82ba768
--- /dev/null
@@ -0,0 +1,30 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 20 May 2024 14:29:58 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: use napi_build_skb()
+
+The napi_build_skb() can reuse the skb in skb cache per CPU or
+can allocate skbs in bulk, which helps improve the performance.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2140,7 +2140,7 @@ static int mtk_poll_rx(struct napi_struc
+                       if (ret != XDP_PASS)
+                               goto skip_rx;
+-                      skb = build_skb(data, PAGE_SIZE);
++                      skb = napi_build_skb(data, PAGE_SIZE);
+                       if (unlikely(!skb)) {
+                               page_pool_put_full_page(ring->page_pool,
+                                                       page, true);
+@@ -2178,7 +2178,7 @@ static int mtk_poll_rx(struct napi_struc
+                       dma_unmap_single(eth->dma_dev, ((u64)trxd.rxd1 | addr64),
+                                        ring->buf_size, DMA_FROM_DEVICE);
+-                      skb = build_skb(data, ring->frag_size);
++                      skb = napi_build_skb(data, ring->frag_size);
+                       if (unlikely(!skb)) {
+                               netdev->stats.rx_dropped++;
+                               skb_free_frag(data);
diff --git a/target/linux/generic/pending-6.6/734-net-ethernet-mediatek-enlarge-DMA-reserve-buffer.patch b/target/linux/generic/pending-6.6/734-net-ethernet-mediatek-enlarge-DMA-reserve-buffer.patch
new file mode 100644 (file)
index 0000000..d786b46
--- /dev/null
@@ -0,0 +1,44 @@
+From: Chad Monroe <chad@monroe.io>
+Date: Mon, 16 Sep 2024 19:29:03 -0700
+Subject: [PATCH] net: ethernet: mediatek: increase QDMA RESV_BUF size
+
+Increase QDMA RESV_BUF from 2K to 3K for netsys v2 to match Mediatek SDK[1].
+This helps reduce the possibility of Ethernet transmit timeouts.
+
+[1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/19d8456c3051e5f6dabf42fa770916a2126ea4bf
+
+Signed-off-by: Chad Monroe <chad@monroe.io>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |    6 ++++--
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h |    1 +
+ 2 files changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -271,6 +271,7 @@
+ #define MTK_WCOMP_EN          BIT(24)
+ #define MTK_RESV_BUF          (0x80 << 16)
+ #define MTK_MUTLI_CNT         (0x4 << 12)
++#define MTK_RESV_BUF_MASK     (0xff << 16)
+ #define MTK_LEAKY_BUCKET_EN   BIT(11)
+ /* QDMA Flow Control Register */
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -3309,12 +3309,14 @@ static int mtk_start_dma(struct mtk_eth
+                      MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO |
+                      MTK_RX_2B_OFFSET | MTK_TX_WB_DDONE;
+-              if (mtk_is_netsys_v2_or_greater(eth))
++              if (mtk_is_netsys_v2_or_greater(eth)) {
++                      val &= ~MTK_RESV_BUF_MASK;
+                       val |= MTK_MUTLI_CNT | MTK_RESV_BUF |
+                              MTK_WCOMP_EN | MTK_DMAD_WR_WDONE |
+                              MTK_CHK_DDONE_EN | MTK_LEAKY_BUCKET_EN;
+-              else
++              } else {
+                       val |= MTK_RX_BT_32DWORDS;
++              }
+               mtk_w32(eth, val, reg_map->qdma.glo_cfg);
+               mtk_w32(eth,
diff --git a/target/linux/generic/pending-6.6/735-net-ethernet-mtk_eth_soc-fix-memory-corruption-durin.patch b/target/linux/generic/pending-6.6/735-net-ethernet-mtk_eth_soc-fix-memory-corruption-durin.patch
new file mode 100644 (file)
index 0000000..5d7902b
--- /dev/null
@@ -0,0 +1,24 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Tue, 15 Oct 2024 10:13:55 +0200
+Subject: [PATCH] net: ethernet: mtk_eth_soc: fix memory corruption during fq
+ dma init
+
+The loop responsible for allocating up to MTK_FQ_DMA_LENGTH buffers must
+only touch as many descriptors, otherwise it ends up corrupting unrelated
+memory. Fix the loop iteration count accordingly.
+
+Fixes: c57e55819443 ("net: ethernet: mtk_eth_soc: handle dma buffer size soc specific")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1172,7 +1172,7 @@ static int mtk_init_fq_dma(struct mtk_et
+               if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr)))
+                       return -ENOMEM;
+-              for (i = 0; i < cnt; i++) {
++              for (i = 0; i < len; i++) {
+                       struct mtk_tx_dma_v2 *txd;
+                       txd = eth->scratch_ring + (j * MTK_FQ_DMA_LENGTH + i) * soc->tx.desc_size;
diff --git a/target/linux/generic/pending-6.6/736-net-ethernet-mtk_wed-fix-path-of-MT7988-WO-firmware.patch b/target/linux/generic/pending-6.6/736-net-ethernet-mtk_wed-fix-path-of-MT7988-WO-firmware.patch
new file mode 100644 (file)
index 0000000..c82f351
--- /dev/null
@@ -0,0 +1,59 @@
+From patchwork Sat Oct 26 13:52:25 2024
+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: 13852245
+X-Patchwork-Delegate: kuba@kernel.org
+Date: Sat, 26 Oct 2024 14:52:25 +0100
+From: Daniel Golle <daniel@makrotopia.org>
+To: linux-mediatek@lists.infradead.org,
+       linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org,
+       netdev@vger.kernel.org, Sujuan Chen <sujuan.chen@mediatek.com>,
+       AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>,
+       Matthias Brugger <matthias.bgg@gmail.com>,
+       Paolo Abeni <pabeni@redhat.com>, Jakub Kicinski <kuba@kernel.org>,
+       Eric Dumazet <edumazet@google.com>,
+       "David S. Miller" <davem@davemloft.net>,
+       Andrew Lunn <andrew+netdev@lunn.ch>,
+       Lorenzo Bianconi <lorenzo@kernel.org>,
+       Mark Lee <Mark-MC.Lee@mediatek.com>,
+       Sean Wang <sean.wang@mediatek.com>, Felix Fietkau <nbd@nbd.name>,
+       John Crispin <john@phrozen.org>
+Subject: [PATCH net] net: ethernet: mtk_wed: fix path of MT7988 WO firmware
+Message-ID: <Zxz0GWTR5X5LdWPe@pidgin.makrotopia.org>
+Precedence: bulk
+X-Mailing-List: netdev@vger.kernel.org
+List-Id: <netdev.vger.kernel.org>
+List-Subscribe: <mailto:netdev+subscribe@vger.kernel.org>
+List-Unsubscribe: <mailto:netdev+unsubscribe@vger.kernel.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+X-Patchwork-Delegate: kuba@kernel.org
+
+linux-firmware commit 808cba84 ("mtk_wed: add firmware for mt7988
+Wireless Ethernet Dispatcher") added mt7988_wo_{0,1}.bin in the
+'mediatek/mt7988' directory while driver current expects the files in
+the 'mediatek' directory.
+
+Change path in the driver header now that the firmware has been added.
+
+Fixes: e2f64db13aa1 ("net: ethernet: mtk_wed: introduce WED support for MT7988")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/ethernet/mediatek/mtk_wed_wo.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.h
+@@ -91,8 +91,8 @@ enum mtk_wed_dummy_cr_idx {
+ #define MT7981_FIRMWARE_WO    "mediatek/mt7981_wo.bin"
+ #define MT7986_FIRMWARE_WO0   "mediatek/mt7986_wo_0.bin"
+ #define MT7986_FIRMWARE_WO1   "mediatek/mt7986_wo_1.bin"
+-#define MT7988_FIRMWARE_WO0   "mediatek/mt7988_wo_0.bin"
+-#define MT7988_FIRMWARE_WO1   "mediatek/mt7988_wo_1.bin"
++#define MT7988_FIRMWARE_WO0   "mediatek/mt7988/mt7988_wo_0.bin"
++#define MT7988_FIRMWARE_WO1   "mediatek/mt7988/mt7988_wo_1.bin"
+ #define MTK_WO_MCU_CFG_LS_BASE                                0
+ #define MTK_WO_MCU_CFG_LS_HW_VER_ADDR                 (MTK_WO_MCU_CFG_LS_BASE + 0x000)
diff --git a/target/linux/generic/pending-6.6/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch b/target/linux/generic/pending-6.6/737-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch
new file mode 100644 (file)
index 0000000..f4e12ca
--- /dev/null
@@ -0,0 +1,936 @@
+From d5e337e7aecc2e1cc9e96768062610adb95f8f72 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 12 Dec 2023 03:51:14 +0000
+Subject: [PATCH] net: ethernet: mtk_eth_soc: add paths and SerDes modes for
+ MT7988
+
+MT7988 comes with a built-in 2.5G PHY as well as SerDes lanes to
+connect external PHYs or transceivers in USXGMII, 10GBase-R, 5GBase-R,
+2500Base-X, 1000Base-X and Cisco SGMII interface modes.
+
+Implement support for configuring for the new paths to SerDes interfaces
+and the internal 2.5G PHY.
+
+Add USXGMII PCS driver for 10GBase-R, 5GBase-R and USXGMII mode, and
+setup the new PHYA on MT7988 to access the also still existing old
+LynxI PCS for 1000Base-X, 2500Base-X and Cisco SGMII PCS interface
+modes.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_path.c | 122 +++++++-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c  | 292 +++++++++++++++++--
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h  | 107 ++++++-
+ 3 files changed, 470 insertions(+), 51 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_eth_path.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c
+@@ -31,10 +31,20 @@ static const char *mtk_eth_path_name(u64
+               return "gmac2_rgmii";
+       case MTK_ETH_PATH_GMAC2_SGMII:
+               return "gmac2_sgmii";
++      case MTK_ETH_PATH_GMAC2_2P5GPHY:
++              return "gmac2_2p5gphy";
+       case MTK_ETH_PATH_GMAC2_GEPHY:
+               return "gmac2_gephy";
++      case MTK_ETH_PATH_GMAC3_SGMII:
++              return "gmac3_sgmii";
+       case MTK_ETH_PATH_GDM1_ESW:
+               return "gdm1_esw";
++      case MTK_ETH_PATH_GMAC1_USXGMII:
++              return "gmac1_usxgmii";
++      case MTK_ETH_PATH_GMAC2_USXGMII:
++              return "gmac2_usxgmii";
++      case MTK_ETH_PATH_GMAC3_USXGMII:
++              return "gmac3_usxgmii";
+       default:
+               return "unknown path";
+       }
+@@ -127,6 +137,27 @@ static int set_mux_u3_gmac2_to_qphy(stru
+       return 0;
+ }
++static int set_mux_gmac2_to_2p5gphy(struct mtk_eth *eth, u64 path)
++{
++      int ret;
++
++      if (path == MTK_ETH_PATH_GMAC2_2P5GPHY) {
++              ret = regmap_clear_bits(eth->ethsys, ETHSYS_SYSCFG0, SYSCFG0_SGMII_GMAC2_V2);
++              if (ret)
++                      return ret;
++
++              /* Setup mux to 2p5g PHY */
++              ret = regmap_clear_bits(eth->infra, TOP_MISC_NETSYS_PCS_MUX, MUX_G2_USXGMII_SEL);
++              if (ret)
++                      return ret;
++
++              dev_dbg(eth->dev, "path %s in %s updated\n",
++                      mtk_eth_path_name(path), __func__);
++      }
++
++      return 0;
++}
++
+ static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path)
+ {
+       unsigned int val = 0;
+@@ -165,7 +196,48 @@ static int set_mux_gmac1_gmac2_to_sgmii_
+       return 0;
+ }
+-static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, u64 path)
++static int set_mux_gmac123_to_usxgmii(struct mtk_eth *eth, u64 path)
++{
++      unsigned int val = 0;
++      bool updated = true;
++      int mac_id = 0;
++
++      /* Disable SYSCFG1 SGMII */
++      regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
++
++      switch (path) {
++      case MTK_ETH_PATH_GMAC1_USXGMII:
++              val &= ~(u32)SYSCFG0_SGMII_GMAC1_V2;
++              mac_id = MTK_GMAC1_ID;
++              break;
++      case MTK_ETH_PATH_GMAC2_USXGMII:
++              val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2;
++              mac_id = MTK_GMAC2_ID;
++              break;
++      case MTK_ETH_PATH_GMAC3_USXGMII:
++              val &= ~(u32)SYSCFG0_SGMII_GMAC3_V2;
++              mac_id = MTK_GMAC3_ID;
++              break;
++      default:
++              updated = false;
++      };
++
++      if (updated) {
++              regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
++                                 SYSCFG0_SGMII_MASK, val);
++
++              if (mac_id == MTK_GMAC2_ID)
++                      regmap_set_bits(eth->infra, TOP_MISC_NETSYS_PCS_MUX,
++                                      MUX_G2_USXGMII_SEL);
++      }
++
++      dev_dbg(eth->dev, "path %s in %s updated = %d\n",
++              mtk_eth_path_name(path), __func__, updated);
++
++      return 0;
++}
++
++static int set_mux_gmac123_to_gephy_sgmii(struct mtk_eth *eth, u64 path)
+ {
+       unsigned int val = 0;
+       bool updated = true;
+@@ -182,6 +254,9 @@ static int set_mux_gmac12_to_gephy_sgmii
+       case MTK_ETH_PATH_GMAC2_SGMII:
+               val |= SYSCFG0_SGMII_GMAC2_V2;
+               break;
++      case MTK_ETH_PATH_GMAC3_SGMII:
++              val |= SYSCFG0_SGMII_GMAC3_V2;
++              break;
+       default:
+               updated = false;
+       }
+@@ -210,13 +285,25 @@ static const struct mtk_eth_muxc mtk_eth
+               .cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY,
+               .set_path = set_mux_u3_gmac2_to_qphy,
+       }, {
++              .name = "mux_gmac2_to_2p5gphy",
++              .cap_bit = MTK_ETH_MUX_GMAC2_TO_2P5GPHY,
++              .set_path = set_mux_gmac2_to_2p5gphy,
++      }, {
+               .name = "mux_gmac1_gmac2_to_sgmii_rgmii",
+               .cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII,
+               .set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii,
+       }, {
+               .name = "mux_gmac12_to_gephy_sgmii",
+               .cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII,
+-              .set_path = set_mux_gmac12_to_gephy_sgmii,
++              .set_path = set_mux_gmac123_to_gephy_sgmii,
++      }, {
++              .name = "mux_gmac123_to_gephy_sgmii",
++              .cap_bit = MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII,
++              .set_path = set_mux_gmac123_to_gephy_sgmii,
++      }, {
++              .name = "mux_gmac123_to_usxgmii",
++              .cap_bit = MTK_ETH_MUX_GMAC123_TO_USXGMII,
++              .set_path = set_mux_gmac123_to_usxgmii,
+       },
+ };
+@@ -249,12 +336,39 @@ out:
+       return err;
+ }
++int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id)
++{
++      u64 path;
++
++      path = (mac_id == MTK_GMAC1_ID) ?  MTK_ETH_PATH_GMAC1_USXGMII :
++             (mac_id == MTK_GMAC2_ID) ?  MTK_ETH_PATH_GMAC2_USXGMII :
++                                         MTK_ETH_PATH_GMAC3_USXGMII;
++
++      /* Setup proper MUXes along the path */
++      return mtk_eth_mux_setup(eth, path);
++}
++
+ int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id)
+ {
+       u64 path;
+-      path = (mac_id == 0) ?  MTK_ETH_PATH_GMAC1_SGMII :
+-                              MTK_ETH_PATH_GMAC2_SGMII;
++      path = (mac_id == MTK_GMAC1_ID) ? MTK_ETH_PATH_GMAC1_SGMII :
++             (mac_id == MTK_GMAC2_ID) ? MTK_ETH_PATH_GMAC2_SGMII :
++                                        MTK_ETH_PATH_GMAC3_SGMII;
++
++      /* Setup proper MUXes along the path */
++      return mtk_eth_mux_setup(eth, path);
++}
++
++int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id)
++{
++      u64 path = 0;
++
++      if (mac_id == MTK_GMAC2_ID)
++              path = MTK_ETH_PATH_GMAC2_2P5GPHY;
++
++      if (!path)
++              return -EINVAL;
+       /* Setup proper MUXes along the path */
+       return mtk_eth_mux_setup(eth, path);
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -22,6 +22,8 @@
+ #include <linux/pinctrl/devinfo.h>
+ #include <linux/phylink.h>
+ #include <linux/pcs/pcs-mtk-lynxi.h>
++#include <linux/pcs/pcs-mtk-usxgmii.h>
++#include <linux/phy/phy.h>
+ #include <linux/jhash.h>
+ #include <linux/bitfield.h>
+ #include <net/dsa.h>
+@@ -270,12 +272,8 @@ static const char * const mtk_clks_sourc
+       "ethwarp_wocpu2",
+       "ethwarp_wocpu1",
+       "ethwarp_wocpu0",
+-      "top_usxgmii0_sel",
+-      "top_usxgmii1_sel",
+       "top_sgm0_sel",
+       "top_sgm1_sel",
+-      "top_xfi_phy0_xtal_sel",
+-      "top_xfi_phy1_xtal_sel",
+       "top_eth_gmii_sel",
+       "top_eth_refck_50m_sel",
+       "top_eth_sys_200m_sel",
+@@ -518,6 +516,30 @@ static void mtk_setup_bridge_switch(stru
+               MTK_GSW_CFG);
+ }
++static bool mtk_check_gmac23_idle(struct mtk_mac *mac)
++{
++      u32 mac_fsm, gdm_fsm;
++
++      mac_fsm = mtk_r32(mac->hw, MTK_MAC_FSM(mac->id));
++
++      switch (mac->id) {
++      case MTK_GMAC2_ID:
++              gdm_fsm = mtk_r32(mac->hw, MTK_FE_GDM2_FSM);
++              break;
++      case MTK_GMAC3_ID:
++              gdm_fsm = mtk_r32(mac->hw, MTK_FE_GDM3_FSM);
++              break;
++      default:
++              return true;
++      };
++
++      if ((mac_fsm & 0xFFFF0000) == 0x01010000 &&
++          (gdm_fsm & 0xFFFF0000) == 0x00000000)
++              return true;
++
++      return false;
++}
++
+ static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
+                                             phy_interface_t interface)
+ {
+@@ -526,6 +548,21 @@ static struct phylink_pcs *mtk_mac_selec
+       struct mtk_eth *eth = mac->hw;
+       unsigned int sid;
++      if (mtk_is_netsys_v3_or_greater(eth)) {
++              switch (interface) {
++              case PHY_INTERFACE_MODE_1000BASEX:
++              case PHY_INTERFACE_MODE_2500BASEX:
++              case PHY_INTERFACE_MODE_SGMII:
++                      return mac->sgmii_pcs;
++              case PHY_INTERFACE_MODE_5GBASER:
++              case PHY_INTERFACE_MODE_10GBASER:
++              case PHY_INTERFACE_MODE_USXGMII:
++                      return mac->usxgmii_pcs;
++              default:
++                      return NULL;
++              }
++      }
++
+       if (interface == PHY_INTERFACE_MODE_SGMII ||
+           phy_interface_mode_is_8023z(interface)) {
+               sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+@@ -577,7 +614,22 @@ static void mtk_mac_config(struct phylin
+                                       goto init_err;
+                       }
+                       break;
++              case PHY_INTERFACE_MODE_USXGMII:
++              case PHY_INTERFACE_MODE_10GBASER:
++              case PHY_INTERFACE_MODE_5GBASER:
++                      if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) {
++                              err = mtk_gmac_usxgmii_path_setup(eth, mac->id);
++                              if (err)
++                                      goto init_err;
++                      }
++                      break;
+               case PHY_INTERFACE_MODE_INTERNAL:
++                      if (mac->id == MTK_GMAC2_ID &&
++                          MTK_HAS_CAPS(eth->soc->caps, MTK_2P5GPHY)) {
++                              err = mtk_gmac_2p5gphy_path_setup(eth, mac->id);
++                              if (err)
++                                      goto init_err;
++                      }
+                       break;
+               default:
+                       goto err_phy;
+@@ -624,8 +676,6 @@ static void mtk_mac_config(struct phylin
+               val &= ~SYSCFG0_GE_MODE(SYSCFG0_GE_MASK, mac->id);
+               val |= SYSCFG0_GE_MODE(ge_mode, mac->id);
+               regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
+-
+-              mac->interface = state->interface;
+       }
+       /* SGMII */
+@@ -642,21 +692,40 @@ static void mtk_mac_config(struct phylin
+               /* Save the syscfg0 value for mac_finish */
+               mac->syscfg0 = val;
+-      } else if (phylink_autoneg_inband(mode)) {
++      } else if (state->interface != PHY_INTERFACE_MODE_USXGMII &&
++                 state->interface != PHY_INTERFACE_MODE_10GBASER &&
++                 state->interface != PHY_INTERFACE_MODE_5GBASER &&
++                 phylink_autoneg_inband(mode)) {
+               dev_err(eth->dev,
+-                      "In-band mode not supported in non SGMII mode!\n");
++                      "In-band mode not supported in non-SerDes modes!\n");
+               return;
+       }
+       /* Setup gmac */
+-      if (mtk_is_netsys_v3_or_greater(eth) &&
+-          mac->interface == PHY_INTERFACE_MODE_INTERNAL) {
+-              mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id));
+-              mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id));
++      if (mtk_is_netsys_v3_or_greater(eth)) {
++              if (mtk_interface_mode_is_xgmii(state->interface)) {
++                      mtk_w32(mac->hw, MTK_GDMA_XGDM_SEL, MTK_GDMA_EG_CTRL(mac->id));
++                      mtk_w32(mac->hw, MAC_MCR_FORCE_LINK_DOWN, MTK_MAC_MCR(mac->id));
++
++                      if (mac->id == MTK_GMAC1_ID)
++                              mtk_setup_bridge_switch(eth);
++              } else {
++                      mtk_w32(eth, 0, MTK_GDMA_EG_CTRL(mac->id));
+-              mtk_setup_bridge_switch(eth);
++                      /* FIXME: In current hardware design, we have to reset FE
++                       * when swtiching XGDM to GDM. Therefore, here trigger an SER
++                       * to let GDM go back to the initial state.
++                       */
++                      if ((mtk_interface_mode_is_xgmii(mac->interface) ||
++                           mac->interface == PHY_INTERFACE_MODE_NA) &&
++                          !mtk_check_gmac23_idle(mac) &&
++                          !test_bit(MTK_RESETTING, &eth->state))
++                              schedule_work(&eth->pending_work);
++              }
+       }
++      mac->interface = state->interface;
++
+       return;
+ err_phy:
+@@ -669,6 +738,18 @@ init_err:
+               mac->id, phy_modes(state->interface), err);
+ }
++static int mtk_mac_prepare(struct phylink_config *config, unsigned int mode,
++                         phy_interface_t interface)
++{
++      struct mtk_mac *mac = container_of(config, struct mtk_mac,
++                                         phylink_config);
++
++      if (mac->pextp && mac->interface != interface)
++              phy_reset(mac->pextp);
++
++      return 0;
++}
++
+ static int mtk_mac_finish(struct phylink_config *config, unsigned int mode,
+                         phy_interface_t interface)
+ {
+@@ -677,6 +758,10 @@ static int mtk_mac_finish(struct phylink
+       struct mtk_eth *eth = mac->hw;
+       u32 mcr_cur, mcr_new;
++      /* Setup PMA/PMD */
++      if (mac->pextp)
++              phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface);
++
+       /* Enable SGMII */
+       if (interface == PHY_INTERFACE_MODE_SGMII ||
+           phy_interface_mode_is_8023z(interface))
+@@ -701,10 +786,14 @@ static void mtk_mac_link_down(struct phy
+ {
+       struct mtk_mac *mac = container_of(config, struct mtk_mac,
+                                          phylink_config);
+-      u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+-      mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK);
+-      mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
++      if (!mtk_interface_mode_is_xgmii(interface)) {
++              mtk_m32(mac->hw, MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK, 0, MTK_MAC_MCR(mac->id));
++              if (mtk_is_netsys_v3_or_greater(mac->hw))
++                      mtk_m32(mac->hw, MTK_XGMAC_FORCE_LINK(mac->id), 0, MTK_XGMAC_STS(mac->id));
++      } else if (mtk_is_netsys_v3_or_greater(mac->hw) && mac->id != MTK_GMAC1_ID) {
++              mtk_m32(mac->hw, XMAC_MCR_TRX_DISABLE, XMAC_MCR_TRX_DISABLE, MTK_XMAC_MCR(mac->id));
++      }
+ }
+ static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx,
+@@ -776,13 +865,11 @@ static void mtk_set_queue_speed(struct m
+       mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs);
+ }
+-static void mtk_mac_link_up(struct phylink_config *config,
+-                          struct phy_device *phy,
+-                          unsigned int mode, phy_interface_t interface,
+-                          int speed, int duplex, bool tx_pause, bool rx_pause)
++static void mtk_gdm_mac_link_up(struct mtk_mac *mac,
++                              struct phy_device *phy,
++                              unsigned int mode, phy_interface_t interface,
++                              int speed, int duplex, bool tx_pause, bool rx_pause)
+ {
+-      struct mtk_mac *mac = container_of(config, struct mtk_mac,
+-                                         phylink_config);
+       u32 mcr;
+       mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+@@ -816,9 +903,63 @@ static void mtk_mac_link_up(struct phyli
+       mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
+ }
++static void mtk_xgdm_mac_link_up(struct mtk_mac *mac,
++                               struct phy_device *phy,
++                               unsigned int mode, phy_interface_t interface,
++                               int speed, int duplex, bool tx_pause, bool rx_pause)
++{
++      u32 mcr, force_link = 0;
++
++      if (mac->id == MTK_GMAC1_ID)
++              return;
++
++      /* Eliminate the interference(before link-up) caused by PHY noise */
++      mtk_m32(mac->hw, XMAC_LOGIC_RST, 0, MTK_XMAC_LOGIC_RST(mac->id));
++      mdelay(20);
++      mtk_m32(mac->hw, XMAC_GLB_CNTCLR, XMAC_GLB_CNTCLR, MTK_XMAC_CNT_CTRL(mac->id));
++
++      if (mac->interface == PHY_INTERFACE_MODE_INTERNAL || mac->id == MTK_GMAC3_ID)
++              force_link = MTK_XGMAC_FORCE_LINK(mac->id);
++
++      mtk_m32(mac->hw, MTK_XGMAC_FORCE_LINK(mac->id), force_link, MTK_XGMAC_STS(mac->id));
++
++      mcr = mtk_r32(mac->hw, MTK_XMAC_MCR(mac->id));
++      mcr &= ~(XMAC_MCR_FORCE_TX_FC | XMAC_MCR_FORCE_RX_FC | XMAC_MCR_TRX_DISABLE);
++      /* Configure pause modes -
++       * phylink will avoid these for half duplex
++       */
++      if (tx_pause)
++              mcr |= XMAC_MCR_FORCE_TX_FC;
++      if (rx_pause)
++              mcr |= XMAC_MCR_FORCE_RX_FC;
++
++      mtk_w32(mac->hw, mcr, MTK_XMAC_MCR(mac->id));
++}
++
++static void mtk_mac_link_up(struct phylink_config *config,
++                          struct phy_device *phy,
++                          unsigned int mode, phy_interface_t interface,
++                          int speed, int duplex, bool tx_pause, bool rx_pause)
++{
++      struct mtk_mac *mac = container_of(config, struct mtk_mac,
++                                         phylink_config);
++
++      if (mtk_is_netsys_v3_or_greater(mac->hw) && mtk_interface_mode_is_xgmii(interface))
++              mtk_xgdm_mac_link_up(mac, phy, mode, interface, speed, duplex,
++                                   tx_pause, rx_pause);
++      else
++              mtk_gdm_mac_link_up(mac, phy, mode, interface, speed, duplex,
++                                  tx_pause, rx_pause);
++
++      /* Repeat pextp setup to tune link */
++      if (mac->pextp)
++              phy_set_mode_ext(mac->pextp, PHY_MODE_ETHERNET, interface);
++}
++
+ static const struct phylink_mac_ops mtk_phylink_ops = {
+       .mac_select_pcs = mtk_mac_select_pcs,
+       .mac_config = mtk_mac_config,
++      .mac_prepare = mtk_mac_prepare,
+       .mac_finish = mtk_mac_finish,
+       .mac_link_down = mtk_mac_link_down,
+       .mac_link_up = mtk_mac_link_up,
+@@ -3417,6 +3558,9 @@ static int mtk_open(struct net_device *d
+       ppe_num = eth->soc->ppe_num;
++      if (mac->pextp)
++              phy_power_on(mac->pextp);
++
+       err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0);
+       if (err) {
+               netdev_err(dev, "%s: could not attach PHY: %d\n", __func__,
+@@ -3567,6 +3711,9 @@ static int mtk_stop(struct net_device *d
+       for (i = 0; i < ARRAY_SIZE(eth->ppe); i++)
+               mtk_ppe_stop(eth->ppe[i]);
++      if (mac->pextp)
++              phy_power_off(mac->pextp);
++
+       return 0;
+ }
+@@ -4580,6 +4727,7 @@ static const struct net_device_ops mtk_n
+ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ {
+       const __be32 *_id = of_get_property(np, "reg", NULL);
++      struct device_node *pcs_np;
+       phy_interface_t phy_mode;
+       struct phylink *phylink;
+       struct mtk_mac *mac;
+@@ -4616,16 +4764,41 @@ static int mtk_add_mac(struct mtk_eth *e
+       mac->id = id;
+       mac->hw = eth;
+       mac->of_node = np;
++      pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 0);
++      if (pcs_np) {
++              mac->sgmii_pcs = mtk_pcs_lynxi_get(eth->dev, pcs_np);
++              if (IS_ERR(mac->sgmii_pcs)) {
++                      if (PTR_ERR(mac->sgmii_pcs) == -EPROBE_DEFER)
++                              return -EPROBE_DEFER;
+-      err = of_get_ethdev_address(mac->of_node, eth->netdev[id]);
+-      if (err == -EPROBE_DEFER)
+-              return err;
++                      dev_err(eth->dev, "cannot select SGMII PCS, error %ld\n",
++                              PTR_ERR(mac->sgmii_pcs));
++                      return PTR_ERR(mac->sgmii_pcs);
++              }
++      }
+-      if (err) {
+-              /* If the mac address is invalid, use random mac address */
+-              eth_hw_addr_random(eth->netdev[id]);
+-              dev_err(eth->dev, "generated random MAC address %pM\n",
+-                      eth->netdev[id]->dev_addr);
++      pcs_np = of_parse_phandle(mac->of_node, "pcs-handle", 1);
++      if (pcs_np) {
++              mac->usxgmii_pcs = mtk_usxgmii_pcs_get(eth->dev, pcs_np);
++              if (IS_ERR(mac->usxgmii_pcs)) {
++                      if (PTR_ERR(mac->usxgmii_pcs) == -EPROBE_DEFER)
++                              return -EPROBE_DEFER;
++
++                      dev_err(eth->dev, "cannot select USXGMII PCS, error %ld\n",
++                              PTR_ERR(mac->usxgmii_pcs));
++                      return PTR_ERR(mac->usxgmii_pcs);
++              }
++      }
++
++      if (mtk_is_netsys_v3_or_greater(eth) && (mac->sgmii_pcs || mac->usxgmii_pcs)) {
++              mac->pextp = devm_of_phy_get(eth->dev, mac->of_node, NULL);
++              if (IS_ERR(mac->pextp)) {
++                      if (PTR_ERR(mac->pextp) != -EPROBE_DEFER)
++                              dev_err(eth->dev, "cannot get PHY, error %ld\n",
++                                      PTR_ERR(mac->pextp));
++
++                      return PTR_ERR(mac->pextp);
++              }
+       }
+       memset(mac->hwlro_ip, 0, sizeof(mac->hwlro_ip));
+@@ -4708,8 +4881,21 @@ static int mtk_add_mac(struct mtk_eth *e
+               phy_interface_zero(mac->phylink_config.supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+                         mac->phylink_config.supported_interfaces);
++      } else if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_USXGMII)) {
++              mac->phylink_config.mac_capabilities |= MAC_5000FD | MAC_10000FD;
++              __set_bit(PHY_INTERFACE_MODE_5GBASER,
++                        mac->phylink_config.supported_interfaces);
++              __set_bit(PHY_INTERFACE_MODE_10GBASER,
++                        mac->phylink_config.supported_interfaces);
++              __set_bit(PHY_INTERFACE_MODE_USXGMII,
++                        mac->phylink_config.supported_interfaces);
+       }
++      if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_2P5GPHY) &&
++          id == MTK_GMAC2_ID)
++              __set_bit(PHY_INTERFACE_MODE_INTERNAL,
++                        mac->phylink_config.supported_interfaces);
++
+       phylink = phylink_create(&mac->phylink_config,
+                                of_fwnode_handle(mac->of_node),
+                                phy_mode, &mtk_phylink_ops);
+@@ -4760,6 +4946,26 @@ free_netdev:
+       return err;
+ }
++static int mtk_mac_assign_address(struct mtk_eth *eth, int i, bool test_defer_only)
++{
++      int err = of_get_ethdev_address(eth->mac[i]->of_node, eth->netdev[i]);
++
++      if (err == -EPROBE_DEFER)
++              return err;
++
++      if (test_defer_only)
++              return 0;
++
++      if (err) {
++              /* If the mac address is invalid, use random mac address */
++              eth_hw_addr_random(eth->netdev[i]);
++              dev_err(eth->dev, "generated random MAC address %pM\n",
++                      eth->netdev[i]);
++      }
++
++      return 0;
++}
++
+ void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev)
+ {
+       struct net_device *dev, *tmp;
+@@ -4906,7 +5112,8 @@ static int mtk_probe(struct platform_dev
+                       regmap_write(cci, 0, 3);
+       }
+-      if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
++      if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII) &&
++          !mtk_is_netsys_v3_or_greater(eth)) {
+               err = mtk_sgmii_init(eth);
+               if (err)
+@@ -5017,6 +5224,24 @@ static int mtk_probe(struct platform_dev
+               }
+       }
++      for (i = 0; i < MTK_MAX_DEVS; i++) {
++              if (!eth->netdev[i])
++                      continue;
++
++              err = mtk_mac_assign_address(eth, i, true);
++              if (err)
++                      goto err_deinit_hw;
++      }
++
++      for (i = 0; i < MTK_MAX_DEVS; i++) {
++              if (!eth->netdev[i])
++                      continue;
++
++              err = mtk_mac_assign_address(eth, i, false);
++              if (err)
++                      goto err_deinit_hw;
++      }
++
+       if (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_INT)) {
+               err = devm_request_irq(eth->dev, eth->irq[0],
+                                      mtk_handle_irq, 0,
+@@ -5120,6 +5345,11 @@ static int mtk_remove(struct platform_de
+               mtk_stop(eth->netdev[i]);
+               mac = netdev_priv(eth->netdev[i]);
+               phylink_disconnect_phy(mac->phylink);
++              if (mac->sgmii_pcs)
++                      mtk_pcs_lynxi_put(mac->sgmii_pcs);
++
++              if (mac->usxgmii_pcs)
++                      mtk_usxgmii_pcs_put(mac->usxgmii_pcs);
+       }
+       mtk_wed_exit();
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -15,6 +15,7 @@
+ #include <linux/u64_stats_sync.h>
+ #include <linux/refcount.h>
+ #include <linux/phylink.h>
++#include <linux/reset.h>
+ #include <linux/rhashtable.h>
+ #include <linux/dim.h>
+ #include <linux/bitfield.h>
+@@ -505,6 +506,21 @@
+ #define INTF_MODE_RGMII_1000    (TRGMII_MODE | TRGMII_CENTRAL_ALIGNED)
+ #define INTF_MODE_RGMII_10_100  0
++/* XFI Mac control registers */
++#define MTK_XMAC_BASE(x)      (0x12000 + (((x) - 1) * 0x1000))
++#define MTK_XMAC_MCR(x)               (MTK_XMAC_BASE(x))
++#define XMAC_MCR_TRX_DISABLE  0xf
++#define XMAC_MCR_FORCE_TX_FC  BIT(5)
++#define XMAC_MCR_FORCE_RX_FC  BIT(4)
++
++/* XFI Mac logic reset registers */
++#define MTK_XMAC_LOGIC_RST(x) (MTK_XMAC_BASE(x) + 0x10)
++#define XMAC_LOGIC_RST                BIT(0)
++
++/* XFI Mac count global control */
++#define MTK_XMAC_CNT_CTRL(x)  (MTK_XMAC_BASE(x) + 0x100)
++#define XMAC_GLB_CNTCLR               BIT(0)
++
+ /* GPIO port control registers for GMAC 2*/
+ #define GPIO_OD33_CTRL8               0x4c0
+ #define GPIO_BIAS_CTRL                0xed0
+@@ -530,6 +546,7 @@
+ #define SYSCFG0_SGMII_GMAC2    ((3 << 8) & SYSCFG0_SGMII_MASK)
+ #define SYSCFG0_SGMII_GMAC1_V2 BIT(9)
+ #define SYSCFG0_SGMII_GMAC2_V2 BIT(8)
++#define SYSCFG0_SGMII_GMAC3_V2 BIT(7)
+ /* ethernet subsystem clock register */
+@@ -568,6 +585,11 @@
+ #define GEPHY_MAC_SEL          BIT(1)
+ /* Top misc registers */
++#define TOP_MISC_NETSYS_PCS_MUX       0x84
++#define NETSYS_PCS_MUX_MASK   GENMASK(1, 0)
++#define       MUX_G2_USXGMII_SEL      BIT(1)
++#define MUX_HSGMII1_G1_SEL    BIT(0)
++
+ #define USB_PHY_SWITCH_REG    0x218
+ #define QPHY_SEL_MASK         GENMASK(1, 0)
+ #define SGMII_QPHY_SEL                0x2
+@@ -592,6 +614,8 @@
+ #define MT7628_SDM_RBCNT      (MT7628_SDM_OFFSET + 0x10c)
+ #define MT7628_SDM_CS_ERR     (MT7628_SDM_OFFSET + 0x110)
++/* Debug Purpose Register */
++#define MTK_PSE_FQFC_CFG      0x100
+ #define MTK_FE_CDM1_FSM               0x220
+ #define MTK_FE_CDM2_FSM               0x224
+ #define MTK_FE_CDM3_FSM               0x238
+@@ -600,6 +624,11 @@
+ #define MTK_FE_CDM6_FSM               0x328
+ #define MTK_FE_GDM1_FSM               0x228
+ #define MTK_FE_GDM2_FSM               0x22C
++#define MTK_FE_GDM3_FSM               0x23C
++#define MTK_FE_PSE_FREE               0x240
++#define MTK_FE_DROP_FQ                0x244
++#define MTK_FE_DROP_FC                0x248
++#define MTK_FE_DROP_PPE               0x24C
+ #define MTK_MAC_FSM(x)                (0x1010C + ((x) * 0x100))
+@@ -724,12 +753,8 @@ enum mtk_clks_map {
+       MTK_CLK_ETHWARP_WOCPU2,
+       MTK_CLK_ETHWARP_WOCPU1,
+       MTK_CLK_ETHWARP_WOCPU0,
+-      MTK_CLK_TOP_USXGMII_SBUS_0_SEL,
+-      MTK_CLK_TOP_USXGMII_SBUS_1_SEL,
+       MTK_CLK_TOP_SGM_0_SEL,
+       MTK_CLK_TOP_SGM_1_SEL,
+-      MTK_CLK_TOP_XFI_PHY_0_XTAL_SEL,
+-      MTK_CLK_TOP_XFI_PHY_1_XTAL_SEL,
+       MTK_CLK_TOP_ETH_GMII_SEL,
+       MTK_CLK_TOP_ETH_REFCK_50M_SEL,
+       MTK_CLK_TOP_ETH_SYS_200M_SEL,
+@@ -800,19 +825,9 @@ enum mtk_clks_map {
+                                BIT_ULL(MTK_CLK_GP3) | BIT_ULL(MTK_CLK_XGP1) | \
+                                BIT_ULL(MTK_CLK_XGP2) | BIT_ULL(MTK_CLK_XGP3) | \
+                                BIT_ULL(MTK_CLK_CRYPTO) | \
+-                               BIT_ULL(MTK_CLK_SGMII_TX_250M) | \
+-                               BIT_ULL(MTK_CLK_SGMII_RX_250M) | \
+-                               BIT_ULL(MTK_CLK_SGMII2_TX_250M) | \
+-                               BIT_ULL(MTK_CLK_SGMII2_RX_250M) | \
+                                BIT_ULL(MTK_CLK_ETHWARP_WOCPU2) | \
+                                BIT_ULL(MTK_CLK_ETHWARP_WOCPU1) | \
+                                BIT_ULL(MTK_CLK_ETHWARP_WOCPU0) | \
+-                               BIT_ULL(MTK_CLK_TOP_USXGMII_SBUS_0_SEL) | \
+-                               BIT_ULL(MTK_CLK_TOP_USXGMII_SBUS_1_SEL) | \
+-                               BIT_ULL(MTK_CLK_TOP_SGM_0_SEL) | \
+-                               BIT_ULL(MTK_CLK_TOP_SGM_1_SEL) | \
+-                               BIT_ULL(MTK_CLK_TOP_XFI_PHY_0_XTAL_SEL) | \
+-                               BIT_ULL(MTK_CLK_TOP_XFI_PHY_1_XTAL_SEL) | \
+                                BIT_ULL(MTK_CLK_TOP_ETH_GMII_SEL) | \
+                                BIT_ULL(MTK_CLK_TOP_ETH_REFCK_50M_SEL) | \
+                                BIT_ULL(MTK_CLK_TOP_ETH_SYS_200M_SEL) | \
+@@ -946,6 +961,8 @@ enum mkt_eth_capabilities {
+       MTK_RGMII_BIT = 0,
+       MTK_TRGMII_BIT,
+       MTK_SGMII_BIT,
++      MTK_USXGMII_BIT,
++      MTK_2P5GPHY_BIT,
+       MTK_ESW_BIT,
+       MTK_GEPHY_BIT,
+       MTK_MUX_BIT,
+@@ -966,8 +983,11 @@ enum mkt_eth_capabilities {
+       MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT,
+       MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT,
+       MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT,
++      MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT,
+       MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT,
+       MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT,
++      MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT,
++      MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT,
+       /* PATH BITS */
+       MTK_ETH_PATH_GMAC1_RGMII_BIT,
+@@ -975,14 +995,21 @@ enum mkt_eth_capabilities {
+       MTK_ETH_PATH_GMAC1_SGMII_BIT,
+       MTK_ETH_PATH_GMAC2_RGMII_BIT,
+       MTK_ETH_PATH_GMAC2_SGMII_BIT,
++      MTK_ETH_PATH_GMAC2_2P5GPHY_BIT,
+       MTK_ETH_PATH_GMAC2_GEPHY_BIT,
++      MTK_ETH_PATH_GMAC3_SGMII_BIT,
+       MTK_ETH_PATH_GDM1_ESW_BIT,
++      MTK_ETH_PATH_GMAC1_USXGMII_BIT,
++      MTK_ETH_PATH_GMAC2_USXGMII_BIT,
++      MTK_ETH_PATH_GMAC3_USXGMII_BIT,
+ };
+ /* Supported hardware group on SoCs */
+ #define MTK_RGMII             BIT_ULL(MTK_RGMII_BIT)
+ #define MTK_TRGMII            BIT_ULL(MTK_TRGMII_BIT)
+ #define MTK_SGMII             BIT_ULL(MTK_SGMII_BIT)
++#define MTK_USXGMII           BIT_ULL(MTK_USXGMII_BIT)
++#define MTK_2P5GPHY           BIT_ULL(MTK_2P5GPHY_BIT)
+ #define MTK_ESW                       BIT_ULL(MTK_ESW_BIT)
+ #define MTK_GEPHY             BIT_ULL(MTK_GEPHY_BIT)
+ #define MTK_MUX                       BIT_ULL(MTK_MUX_BIT)
+@@ -1005,10 +1032,16 @@ enum mkt_eth_capabilities {
+       BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT)
+ #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY          \
+       BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT)
++#define MTK_ETH_MUX_GMAC2_TO_2P5GPHY          \
++      BIT_ULL(MTK_ETH_MUX_GMAC2_TO_2P5GPHY_BIT)
+ #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII        \
+       BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT)
+ #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII     \
+       BIT_ULL(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT)
++#define MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII    \
++      BIT_ULL(MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT)
++#define MTK_ETH_MUX_GMAC123_TO_USXGMII        \
++      BIT_ULL(MTK_ETH_MUX_GMAC123_TO_USXGMII_BIT)
+ /* Supported path present on SoCs */
+ #define MTK_ETH_PATH_GMAC1_RGMII      BIT_ULL(MTK_ETH_PATH_GMAC1_RGMII_BIT)
+@@ -1016,8 +1049,13 @@ enum mkt_eth_capabilities {
+ #define MTK_ETH_PATH_GMAC1_SGMII      BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT)
+ #define MTK_ETH_PATH_GMAC2_RGMII      BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT)
+ #define MTK_ETH_PATH_GMAC2_SGMII      BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT)
++#define MTK_ETH_PATH_GMAC2_2P5GPHY    BIT_ULL(MTK_ETH_PATH_GMAC2_2P5GPHY_BIT)
+ #define MTK_ETH_PATH_GMAC2_GEPHY      BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT)
++#define MTK_ETH_PATH_GMAC3_SGMII      BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT)
+ #define MTK_ETH_PATH_GDM1_ESW         BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT)
++#define MTK_ETH_PATH_GMAC1_USXGMII    BIT_ULL(MTK_ETH_PATH_GMAC1_USXGMII_BIT)
++#define MTK_ETH_PATH_GMAC2_USXGMII    BIT_ULL(MTK_ETH_PATH_GMAC2_USXGMII_BIT)
++#define MTK_ETH_PATH_GMAC3_USXGMII    BIT_ULL(MTK_ETH_PATH_GMAC3_USXGMII_BIT)
+ #define MTK_GMAC1_RGMII               (MTK_ETH_PATH_GMAC1_RGMII | MTK_RGMII)
+ #define MTK_GMAC1_TRGMII      (MTK_ETH_PATH_GMAC1_TRGMII | MTK_TRGMII)
+@@ -1025,7 +1063,12 @@ enum mkt_eth_capabilities {
+ #define MTK_GMAC2_RGMII               (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII)
+ #define MTK_GMAC2_SGMII               (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII)
+ #define MTK_GMAC2_GEPHY               (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY)
++#define MTK_GMAC2_2P5GPHY     (MTK_ETH_PATH_GMAC2_2P5GPHY | MTK_2P5GPHY)
++#define MTK_GMAC3_SGMII               (MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII)
+ #define MTK_GDM1_ESW          (MTK_ETH_PATH_GDM1_ESW | MTK_ESW)
++#define MTK_GMAC1_USXGMII     (MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII)
++#define MTK_GMAC2_USXGMII     (MTK_ETH_PATH_GMAC2_USXGMII | MTK_USXGMII)
++#define MTK_GMAC3_USXGMII     (MTK_ETH_PATH_GMAC3_USXGMII | MTK_USXGMII)
+ /* MUXes present on SoCs */
+ /* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */
+@@ -1044,10 +1087,20 @@ enum mkt_eth_capabilities {
+       (MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_MUX | \
+       MTK_SHARED_SGMII)
++/* 2: GMAC2 -> XGMII */
++#define MTK_MUX_GMAC2_TO_2P5GPHY      \
++      (MTK_ETH_MUX_GMAC2_TO_2P5GPHY | MTK_MUX | MTK_INFRA)
++
+ /* 0: GMACx -> GEPHY, 1: GMACx -> SGMII where x is 1 or 2 */
+ #define MTK_MUX_GMAC12_TO_GEPHY_SGMII   \
+       (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX)
++#define MTK_MUX_GMAC123_TO_GEPHY_SGMII   \
++      (MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII | MTK_MUX)
++
++#define MTK_MUX_GMAC123_TO_USXGMII   \
++      (MTK_ETH_MUX_GMAC123_TO_USXGMII | MTK_MUX | MTK_INFRA)
++
+ #define MTK_HAS_CAPS(caps, _x)                (((caps) & (_x)) == (_x))
+ #define MT7621_CAPS  (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | \
+@@ -1079,8 +1132,12 @@ enum mkt_eth_capabilities {
+                     MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \
+                     MTK_RSTCTRL_PPE1 | MTK_SRAM)
+-#define MT7988_CAPS  (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_QDMA | \
+-                    MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | MTK_SRAM)
++#define MT7988_CAPS  (MTK_36BIT_DMA | MTK_GDM1_ESW | MTK_GMAC1_SGMII | \
++                    MTK_GMAC2_2P5GPHY | MTK_GMAC2_SGMII | MTK_GMAC2_USXGMII | \
++                    MTK_GMAC3_SGMII | MTK_GMAC3_USXGMII | \
++                    MTK_MUX_GMAC123_TO_GEPHY_SGMII | \
++                    MTK_MUX_GMAC123_TO_USXGMII | MTK_MUX_GMAC2_TO_2P5GPHY | \
++                    MTK_QDMA | MTK_RSTCTRL_PPE1 | MTK_RSTCTRL_PPE2 | MTK_SRAM)
+ struct mtk_tx_dma_desc_info {
+       dma_addr_t      addr;
+@@ -1325,6 +1382,9 @@ struct mtk_mac {
+       struct device_node              *of_node;
+       struct phylink                  *phylink;
+       struct phylink_config           phylink_config;
++      struct phylink_pcs              *sgmii_pcs;
++      struct phylink_pcs              *usxgmii_pcs;
++      struct phy                      *pextp;
+       struct mtk_eth                  *hw;
+       struct mtk_hw_stats             *hw_stats;
+       __be32                          hwlro_ip[MTK_MAX_LRO_IP_CNT];
+@@ -1448,6 +1508,19 @@ static inline u32 mtk_get_ib2_multicast_
+       return MTK_FOE_IB2_MULTICAST;
+ }
++static inline bool mtk_interface_mode_is_xgmii(phy_interface_t interface)
++{
++      switch (interface) {
++      case PHY_INTERFACE_MODE_INTERNAL:
++      case PHY_INTERFACE_MODE_USXGMII:
++      case PHY_INTERFACE_MODE_10GBASER:
++      case PHY_INTERFACE_MODE_5GBASER:
++              return true;
++      default:
++              return false;
++      }
++}
++
+ /* read the hardware status register */
+ void mtk_stats_update_mac(struct mtk_mac *mac);
+@@ -1456,8 +1529,10 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne
+ u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned int reg);
+ int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
++int mtk_gmac_2p5gphy_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
++int mtk_gmac_usxgmii_path_setup(struct mtk_eth *eth, int mac_id);
+ int mtk_eth_offload_init(struct mtk_eth *eth, u8 id);
+ int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type,
diff --git a/target/linux/generic/pending-6.6/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch b/target/linux/generic/pending-6.6/739-01-dt-bindings-phy-mediatek-xfi-tphy-add-new-bindings.patch
new file mode 100644 (file)
index 0000000..1f1c40b
--- /dev/null
@@ -0,0 +1,136 @@
+From patchwork Thu Feb  1 21:52:20 2024
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
+X-Patchwork-Id: 13541842
+Date: Thu, 1 Feb 2024 21:52:20 +0000
+From: Daniel Golle <daniel@makrotopia.org>
+To: Bc-bocun Chen <bc-bocun.chen@mediatek.com>,
+       Steven Liu <steven.liu@mediatek.com>,
+       John Crispin <john@phrozen.org>,
+       Chunfeng Yun <chunfeng.yun@mediatek.com>,
+       Vinod Koul <vkoul@kernel.org>,
+       Kishon Vijay Abraham I <kishon@kernel.org>,
+       Rob Herring <robh@kernel.org>,
+       Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
+       Conor Dooley <conor+dt@kernel.org>,
+       Daniel Golle <daniel@makrotopia.org>,
+       Qingfang Deng <dqfext@gmail.com>,
+       SkyLake Huang <SkyLake.Huang@mediatek.com>,
+       Matthias Brugger <matthias.bgg@gmail.com>,
+       AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>,
+       Philipp Zabel <p.zabel@pengutronix.de>,
+       linux-arm-kernel@lists.infradead.org,
+       linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org,
+       devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
+       netdev@vger.kernel.org
+Subject: [PATCH 1/2] dt-bindings: phy: mediatek,xfi-tphy: add new bindings
+Message-ID: 
+ <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+List-Id: Linux Phy Mailing list <linux-phy.lists.infradead.org>
+
+Add bindings for the MediaTek XFI T-PHY Ethernet SerDes PHY found in the
+MediaTek MT7988 SoC which can operate at various interfaces modes:
+
+via USXGMII PCS:
+ * USXGMII
+ * 10GBase-R
+ * 5GBase-R
+
+via LynxI SGMII PCS:
+ * 2500Base-X
+ * 1000Base-X
+ * Cisco SGMII (MAC side)
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ .../bindings/phy/mediatek,xfi-tphy.yaml       | 80 +++++++++++++++++++
+ 1 file changed, 80 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/phy/mediatek,xfi-tphy.yaml
+@@ -0,0 +1,80 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/phy/mediatek,xfi-tphy.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: MediaTek XFI T-PHY
++
++maintainers:
++  - Daniel Golle <daniel@makrotopia.org>
++
++description:
++  The MediaTek XFI SerDes T-PHY provides the physical SerDes lanes
++  used by the (10G/5G) USXGMII PCS and (1G/2.5G) LynxI PCS found in
++  MediaTek's 10G-capabale SoCs.
++
++properties:
++  $nodename:
++    pattern: "^phy@[0-9a-f]+$"
++
++  compatible:
++    const: mediatek,mt7988-xfi-tphy
++
++  reg:
++    maxItems: 1
++
++  clocks:
++    items:
++      - description: XFI PHY clock
++      - description: XFI register clock
++
++  clock-names:
++    items:
++      - const: xfipll
++      - const: topxtal
++
++  resets:
++    items:
++      - description: PEXTP reset
++
++  mediatek,usxgmii-performance-errata:
++    $ref: /schemas/types.yaml#/definitions/flag
++    description:
++      One instance of the T-PHY on MT7988 suffers from a performance
++      problem in 10GBase-R mode which needs a work-around in the driver.
++      The work-around is enabled using this flag.
++
++  "#phy-cells":
++    const: 0
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - clock-names
++  - resets
++  - "#phy-cells"
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/mediatek,mt7988-clk.h>
++    soc {
++      #address-cells = <2>;
++      #size-cells = <2>;
++
++      phy@11f20000 {
++        compatible = "mediatek,mt7988-xfi-tphy";
++        reg = <0 0x11f20000 0 0x10000>;
++        clocks = <&xfi_pll CLK_XFIPLL_PLL_EN>,
++                 <&topckgen CLK_TOP_XFI_PHY_0_XTAL_SEL>;
++        clock-names = "xfipll", "topxtal";
++        resets = <&watchdog 14>;
++        mediatek,usxgmii-performance-errata;
++        #phy-cells = <0>;
++      };
++    };
++
++...
diff --git a/target/linux/generic/pending-6.6/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch b/target/linux/generic/pending-6.6/739-02-phy-add-driver-for-MediaTek-XFI-T-PHY.patch
new file mode 100644 (file)
index 0000000..1aa36fc
--- /dev/null
@@ -0,0 +1,498 @@
+From patchwork Thu Feb  1 21:53:06 2024
+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: 13541843
+Date: Thu, 1 Feb 2024 21:53:06 +0000
+From: Daniel Golle <daniel@makrotopia.org>
+To: Bc-bocun Chen <bc-bocun.chen@mediatek.com>,
+       Chunfeng Yun <chunfeng.yun@mediatek.com>,
+       Vinod Koul <vkoul@kernel.org>,
+       Kishon Vijay Abraham I <kishon@kernel.org>,
+       Rob Herring <robh@kernel.org>,
+       Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
+       Conor Dooley <conor+dt@kernel.org>,
+       Daniel Golle <daniel@makrotopia.org>,
+       Qingfang Deng <dqfext@gmail.com>,
+       SkyLake Huang <SkyLake.Huang@mediatek.com>,
+       Matthias Brugger <matthias.bgg@gmail.com>,
+       AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>,
+       Philipp Zabel <p.zabel@pengutronix.de>,
+       linux-arm-kernel@lists.infradead.org,
+       linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org,
+       devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
+       netdev@vger.kernel.org
+Subject: [PATCH 2/2] phy: add driver for MediaTek XFI T-PHY
+Message-ID: 
+ <dd6b40ea1f7f8459a9a2cfe7fa60c1108332ade6.1706823233.git.daniel@makrotopia.org>
+References: 
+ <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org>
+MIME-Version: 1.0
+Content-Disposition: inline
+In-Reply-To: 
+ <702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org>
+List-Id: Linux Phy Mailing list <linux-phy.lists.infradead.org>
+
+Add driver for MediaTek's XFI T-PHY, 10 Gigabit/s Ethernet SerDes PHY
+which can be found in the MT7988 SoC.
+
+The PHY can operates only in PHY_MODE_ETHERNET, the submode is one of
+PHY_INTERFACE_MODE_* corresponding to the supported modes:
+
+ * USXGMII                 \
+ * 10GBase-R                }- USXGMII PCS - XGDM  \
+ * 5GBase-R                /                        \
+                                                     }- Ethernet MAC
+ * 2500Base-X              \                        /
+ * 1000Base-X               }- LynxI PCS - GDM     /
+ * Cisco SGMII (MAC side)  /
+
+In order to work-around a performance issue present on the first of
+two XFI T-PHYs present in MT7988, special tuning is applied which can be
+selected by adding the 'mediatek,usxgmii-performance-errata' property to
+the device tree node.
+
+There is no documentation for most registers used for the
+analog/tuning part, however, most of the registers have been partially
+reverse-engineered from MediaTek's SDK implementation (an opaque
+sequence of 32-bit register writes) and descriptions for all relevant
+digital registers and bits such as resets and muxes have been supplied
+by MediaTek.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ MAINTAINERS                             |   1 +
+ drivers/phy/mediatek/Kconfig            |  12 +
+ drivers/phy/mediatek/Makefile           |   1 +
+ drivers/phy/mediatek/phy-mtk-xfi-tphy.c | 392 ++++++++++++++++++++++++
+ 4 files changed, 406 insertions(+)
+ create mode 100644 drivers/phy/mediatek/phy-mtk-xfi-tphy.c
+
+--- a/drivers/phy/mediatek/Kconfig
++++ b/drivers/phy/mediatek/Kconfig
+@@ -13,6 +13,18 @@ config PHY_MTK_PCIE
+         callback for PCIe GEN3 port, it supports software efuse
+         initialization.
++config PHY_MTK_XFI_TPHY
++      tristate "MediaTek XFI T-PHY Driver"
++      depends on ARCH_MEDIATEK || COMPILE_TEST
++      depends on OF && OF_ADDRESS
++      depends on HAS_IOMEM
++      select GENERIC_PHY
++      help
++        Say 'Y' here to add support for MediaTek XFI T-PHY driver.
++        The driver provides access to the Ethernet SerDes T-PHY supporting
++        1GE and 2.5GE modes via the LynxI PCS, and 5GE and 10GE modes
++        via the USXGMII PCS found in MediaTek SoCs with 10G Ethernet.
++
+ config PHY_MTK_TPHY
+       tristate "MediaTek T-PHY Driver"
+       depends on ARCH_MEDIATEK || COMPILE_TEST
+--- a/drivers/phy/mediatek/Makefile
++++ b/drivers/phy/mediatek/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_MTK_PCIE)             += phy-mtk-p
+ obj-$(CONFIG_PHY_MTK_TPHY)            += phy-mtk-tphy.o
+ obj-$(CONFIG_PHY_MTK_UFS)             += phy-mtk-ufs.o
+ obj-$(CONFIG_PHY_MTK_XSPHY)           += phy-mtk-xsphy.o
++obj-$(CONFIG_PHY_MTK_XFI_TPHY)                += phy-mtk-xfi-tphy.o
+ phy-mtk-hdmi-drv-y                    := phy-mtk-hdmi.o
+ phy-mtk-hdmi-drv-y                    += phy-mtk-hdmi-mt2701.o
+--- /dev/null
++++ b/drivers/phy/mediatek/phy-mtk-xfi-tphy.c
+@@ -0,0 +1,393 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/* MediaTek 10GE SerDes PHY driver
++ *
++ * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
++ *                    Bc-bocun Chen <bc-bocun.chen@mediatek.com>
++ * based on mtk_usxgmii.c found in MediaTek's SDK released under GPL-2.0
++ * Copyright (c) 2022 MediaTek Inc.
++ * Author: Henry Yen <henry.yen@mediatek.com>
++ */
++
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++#include <linux/phy.h>
++#include <linux/phy/phy.h>
++
++#define MTK_XFI_TPHY_NUM_CLOCKS               2
++
++#define REG_DIG_GLB_70                        0x0070
++#define  XTP_PCS_RX_EQ_IN_PROGRESS(x) FIELD_PREP(GENMASK(25, 24), (x))
++#define  XTP_PCS_MODE_MASK            GENMASK(17, 16)
++#define  XTP_PCS_MODE(x)              FIELD_PREP(GENMASK(17, 16), (x))
++#define  XTP_PCS_RST_B                        BIT(15)
++#define  XTP_FRC_PCS_RST_B            BIT(14)
++#define  XTP_PCS_PWD_SYNC_MASK                GENMASK(13, 12)
++#define  XTP_PCS_PWD_SYNC(x)          FIELD_PREP(XTP_PCS_PWD_SYNC_MASK, (x))
++#define  XTP_PCS_PWD_ASYNC_MASK               GENMASK(11, 10)
++#define  XTP_PCS_PWD_ASYNC(x)         FIELD_PREP(XTP_PCS_PWD_ASYNC_MASK, (x))
++#define  XTP_FRC_PCS_PWD_ASYNC                BIT(8)
++#define  XTP_PCS_UPDT                 BIT(4)
++#define  XTP_PCS_IN_FR_RG             BIT(0)
++
++#define REG_DIG_GLB_F4                        0x00f4
++#define  XFI_DPHY_PCS_SEL             BIT(0)
++#define   XFI_DPHY_PCS_SEL_SGMII      FIELD_PREP(XFI_DPHY_PCS_SEL, 1)
++#define   XFI_DPHY_PCS_SEL_USXGMII    FIELD_PREP(XFI_DPHY_PCS_SEL, 0)
++#define  XFI_DPHY_AD_SGDT_FRC_EN      BIT(5)
++
++#define REG_DIG_LN_TRX_40             0x3040
++#define  XTP_LN_FRC_TX_DATA_EN                BIT(29)
++#define  XTP_LN_TX_DATA_EN            BIT(28)
++
++#define REG_DIG_LN_TRX_B0             0x30b0
++#define  XTP_LN_FRC_TX_MACCK_EN               BIT(5)
++#define  XTP_LN_TX_MACCK_EN           BIT(4)
++
++#define REG_ANA_GLB_D0                        0x90d0
++#define  XTP_GLB_USXGMII_SEL_MASK     GENMASK(3, 1)
++#define  XTP_GLB_USXGMII_SEL(x)               FIELD_PREP(GENMASK(3, 1), (x))
++#define  XTP_GLB_USXGMII_EN           BIT(0)
++
++struct mtk_xfi_tphy {
++      void __iomem            *base;
++      struct device           *dev;
++      struct reset_control    *reset;
++      struct clk_bulk_data    clocks[MTK_XFI_TPHY_NUM_CLOCKS];
++      bool                    da_war;
++};
++
++static void mtk_xfi_tphy_write(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
++                             u32 value)
++{
++      iowrite32(value, xfi_tphy->base + reg);
++}
++
++static void mtk_xfi_tphy_rmw(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
++                           u32 clr, u32 set)
++{
++      u32 val;
++
++      val = ioread32(xfi_tphy->base + reg);
++      val &= ~clr;
++      val |= set;
++      iowrite32(val, xfi_tphy->base + reg);
++}
++
++static void mtk_xfi_tphy_set(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
++                           u32 set)
++{
++      mtk_xfi_tphy_rmw(xfi_tphy, reg, 0, set);
++}
++
++static void mtk_xfi_tphy_clear(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
++                             u32 clr)
++{
++      mtk_xfi_tphy_rmw(xfi_tphy, reg, clr, 0);
++}
++
++static void mtk_xfi_tphy_setup(struct mtk_xfi_tphy *xfi_tphy,
++                             phy_interface_t interface)
++{
++      bool is_2p5g = (interface == PHY_INTERFACE_MODE_2500BASEX);
++      bool is_1g = (interface == PHY_INTERFACE_MODE_1000BASEX ||
++                    interface == PHY_INTERFACE_MODE_SGMII);
++      bool is_10g = (interface == PHY_INTERFACE_MODE_10GBASER ||
++                     interface == PHY_INTERFACE_MODE_USXGMII);
++      bool is_5g = (interface == PHY_INTERFACE_MODE_5GBASER);
++      bool is_xgmii = (is_10g || is_5g);
++
++      dev_dbg(xfi_tphy->dev, "setting up for mode %s\n", phy_modes(interface));
++
++      /* Setup PLL setting */
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x9024, 0x100000, is_10g ? 0x0 : 0x100000);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x2020, 0x202000, is_5g ? 0x202000 : 0x0);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x2030, 0x500, is_1g ? 0x0 : 0x500);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x2034, 0xa00, is_1g ? 0x0 : 0xa00);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x2040, 0x340000, is_1g ? 0x200000 :
++                                                           0x140000);
++
++      /* Setup RXFE BW setting */
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x50f0, 0xc10, is_1g ? 0x410 :
++                                                        is_5g ? 0x800 : 0x400);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x50e0, 0x4000, is_5g ? 0x0 : 0x4000);
++
++      /* Setup RX CDR setting */
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x506c, 0x30000, is_5g ? 0x0 : 0x30000);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x5070, 0x670000, is_5g ? 0x620000 : 0x50000);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x5074, 0x180000, is_5g ? 0x180000 : 0x0);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x5078, 0xf000400, is_5g ? 0x8000000 :
++                                                            0x7000400);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x507c, 0x5000500, is_5g ? 0x4000400 :
++                                                            0x1000100);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x5080, 0x1410, is_1g ? 0x400 :
++                                                         is_5g ? 0x1010 : 0x0);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x5084, 0x30300, is_1g ? 0x30300 :
++                                                          is_5g ? 0x30100 :
++                                                                  0x100);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x5088, 0x60200, is_1g ? 0x20200 :
++                                                       is_5g ? 0x40000 :
++                                                               0x20000);
++
++      /* Setting RXFE adaptation range setting */
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x50e4, 0xc0000, is_5g ? 0x0 : 0xc0000);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x50e8, 0x40000, is_5g ? 0x0 : 0x40000);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x50ec, 0xa00, is_1g ? 0x200 : 0x800);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x50a8, 0xee0000, is_5g ? 0x800000 :
++                                                           0x6e0000);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x6004, 0x190000, is_5g ? 0x0 : 0x190000);
++      if (is_10g)
++              mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x01423342);
++      else if (is_5g)
++              mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00a132a1);
++      else if (is_2p5g)
++              mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x009c329c);
++      else
++              mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00fa32fa);
++
++      /* Force SGDT_OUT off and select PCS */
++      mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_F4,
++                       XFI_DPHY_AD_SGDT_FRC_EN | XFI_DPHY_PCS_SEL,
++                       XFI_DPHY_AD_SGDT_FRC_EN |
++                       (is_xgmii ? XFI_DPHY_PCS_SEL_USXGMII :
++                                   XFI_DPHY_PCS_SEL_SGMII));
++
++
++      /* Force GLB_CKDET_OUT */
++      mtk_xfi_tphy_set(xfi_tphy, 0x0030, 0xc00);
++
++      /* Force AEQ on */
++      mtk_xfi_tphy_write(xfi_tphy, REG_DIG_GLB_70,
++                         XTP_PCS_RX_EQ_IN_PROGRESS(2) |
++                         XTP_PCS_PWD_SYNC(2) |
++                         XTP_PCS_PWD_ASYNC(2));
++
++      usleep_range(1, 5);
++      writel(XTP_LN_FRC_TX_DATA_EN, xfi_tphy->base + REG_DIG_LN_TRX_40);
++
++      /* Setup TX DA default value */
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x30b0, 0x30, 0x20);
++      mtk_xfi_tphy_write(xfi_tphy, 0x3028, 0x00008a01);
++      mtk_xfi_tphy_write(xfi_tphy, 0x302c, 0x0000a884);
++      mtk_xfi_tphy_write(xfi_tphy, 0x3024, 0x00083002);
++
++      /* Setup RG default value */
++      if (is_xgmii) {
++              mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00022220);
++              mtk_xfi_tphy_write(xfi_tphy, 0x5064, 0x0f020a01);
++              mtk_xfi_tphy_write(xfi_tphy, 0x50b4, 0x06100600);
++              if (interface == PHY_INTERFACE_MODE_USXGMII)
++                      mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000);
++              else
++                      mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x47684100);
++      } else {
++              mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00011110);
++              mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000);
++      }
++
++      if (is_1g)
++              mtk_xfi_tphy_write(xfi_tphy, 0x3064, 0x0000c000);
++
++      /* Setup RX EQ initial value */
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x3050, 0xa8000000,
++                       (interface != PHY_INTERFACE_MODE_10GBASER) ?
++                        0xa8000000 : 0x0);
++      mtk_xfi_tphy_rmw(xfi_tphy, 0x3054, 0xaa,
++                       (interface != PHY_INTERFACE_MODE_10GBASER) ?
++                        0xaa : 0x0);
++
++      if (is_xgmii)
++              mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x00000f00);
++      else if (is_2p5g)
++              mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x22000f00);
++      else
++              mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x20200f00);
++
++      if (interface == PHY_INTERFACE_MODE_10GBASER && xfi_tphy->da_war)
++              mtk_xfi_tphy_rmw(xfi_tphy, 0xa008, 0x10000, 0x10000);
++
++      mtk_xfi_tphy_rmw(xfi_tphy, 0xa060, 0x50000, is_xgmii ? 0x40000 :
++                                                             0x50000);
++
++      /* Setup PHYA speed */
++      mtk_xfi_tphy_rmw(xfi_tphy, REG_ANA_GLB_D0,
++                       XTP_GLB_USXGMII_SEL_MASK | XTP_GLB_USXGMII_EN,
++                       is_10g ?  XTP_GLB_USXGMII_SEL(0) :
++                       is_5g ?   XTP_GLB_USXGMII_SEL(1) :
++                       is_2p5g ? XTP_GLB_USXGMII_SEL(2) :
++                                 XTP_GLB_USXGMII_SEL(3));
++      mtk_xfi_tphy_set(xfi_tphy, REG_ANA_GLB_D0, XTP_GLB_USXGMII_EN);
++
++      /* Release reset */
++      mtk_xfi_tphy_set(xfi_tphy, REG_DIG_GLB_70,
++                       XTP_PCS_RST_B | XTP_FRC_PCS_RST_B);
++      usleep_range(150, 500);
++
++      /* Switch to P0 */
++      mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70,
++                       XTP_PCS_PWD_SYNC_MASK |
++                       XTP_PCS_PWD_ASYNC_MASK,
++                       XTP_FRC_PCS_PWD_ASYNC |
++                       XTP_PCS_UPDT | XTP_PCS_IN_FR_RG);
++      usleep_range(1, 5);
++
++      mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT);
++      usleep_range(15, 50);
++
++      if (is_xgmii) {
++              /* Switch to Gen3 */
++              mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70,
++                               XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
++                               XTP_PCS_MODE(2) | XTP_PCS_UPDT);
++      } else {
++              /* Switch to Gen2 */
++              mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70,
++                               XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
++                               XTP_PCS_MODE(1) | XTP_PCS_UPDT);
++      }
++      usleep_range(1, 5);
++
++      mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT);
++
++      usleep_range(100, 500);
++
++      /* Enable MAC CK */
++      mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_B0, XTP_LN_TX_MACCK_EN);
++      mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_F4, XFI_DPHY_AD_SGDT_FRC_EN);
++
++      /* Enable TX data */
++      mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_40,
++                       XTP_LN_FRC_TX_DATA_EN | XTP_LN_TX_DATA_EN);
++      usleep_range(400, 1000);
++}
++
++static int mtk_xfi_tphy_set_mode(struct phy *phy, enum phy_mode mode, int
++                               submode)
++{
++      struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
++
++      if (mode != PHY_MODE_ETHERNET)
++              return -EINVAL;
++
++      switch (submode) {
++      case PHY_INTERFACE_MODE_1000BASEX:
++      case PHY_INTERFACE_MODE_2500BASEX:
++      case PHY_INTERFACE_MODE_SGMII:
++      case PHY_INTERFACE_MODE_5GBASER:
++      case PHY_INTERFACE_MODE_10GBASER:
++      case PHY_INTERFACE_MODE_USXGMII:
++              mtk_xfi_tphy_setup(xfi_tphy, submode);
++              return 0;
++      default:
++              return -EINVAL;
++      }
++}
++
++static int mtk_xfi_tphy_reset(struct phy *phy)
++{
++      struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
++
++      reset_control_assert(xfi_tphy->reset);
++      usleep_range(100, 500);
++      reset_control_deassert(xfi_tphy->reset);
++      usleep_range(1, 10);
++
++      return 0;
++}
++
++static int mtk_xfi_tphy_power_on(struct phy *phy)
++{
++      struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
++
++      return clk_bulk_prepare_enable(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
++}
++
++static int mtk_xfi_tphy_power_off(struct phy *phy)
++{
++      struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
++
++      clk_bulk_disable_unprepare(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
++
++      return 0;
++}
++
++static const struct phy_ops mtk_xfi_tphy_ops = {
++      .power_on       = mtk_xfi_tphy_power_on,
++      .power_off      = mtk_xfi_tphy_power_off,
++      .set_mode       = mtk_xfi_tphy_set_mode,
++      .reset          = mtk_xfi_tphy_reset,
++      .owner          = THIS_MODULE,
++};
++
++static int mtk_xfi_tphy_probe(struct platform_device *pdev)
++{
++      struct device_node *np = pdev->dev.of_node;
++      struct phy_provider *phy_provider;
++      struct mtk_xfi_tphy *xfi_tphy;
++      struct phy *phy;
++
++      if (!np)
++              return -ENODEV;
++
++      xfi_tphy = devm_kzalloc(&pdev->dev, sizeof(*xfi_tphy), GFP_KERNEL);
++      if (!xfi_tphy)
++              return -ENOMEM;
++
++      xfi_tphy->base = devm_of_iomap(&pdev->dev, np, 0, NULL);
++      if (!xfi_tphy->base)
++              return -EIO;
++
++      xfi_tphy->dev = &pdev->dev;
++
++      xfi_tphy->clocks[0].id = "topxtal";
++      xfi_tphy->clocks[0].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[0].id);
++      if (IS_ERR(xfi_tphy->clocks[0].clk))
++              return PTR_ERR(xfi_tphy->clocks[0].clk);
++
++      xfi_tphy->clocks[1].id = "xfipll";
++      xfi_tphy->clocks[1].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[1].id);
++      if (IS_ERR(xfi_tphy->clocks[1].clk))
++              return PTR_ERR(xfi_tphy->clocks[1].clk);
++
++      xfi_tphy->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
++      if (IS_ERR(xfi_tphy->reset))
++              return PTR_ERR(xfi_tphy->reset);
++
++      xfi_tphy->da_war = of_property_read_bool(np,
++                                               "mediatek,usxgmii-performance-errata");
++
++      phy = devm_phy_create(&pdev->dev, NULL, &mtk_xfi_tphy_ops);
++      if (IS_ERR(phy))
++              return PTR_ERR(phy);
++
++      phy_set_drvdata(phy, xfi_tphy);
++
++      phy_provider = devm_of_phy_provider_register(&pdev->dev,
++                                                   of_phy_simple_xlate);
++
++      return PTR_ERR_OR_ZERO(phy_provider);
++}
++
++static const struct of_device_id mtk_xfi_tphy_match[] = {
++      { .compatible = "mediatek,mt7988-xfi-tphy", },
++      { }
++};
++MODULE_DEVICE_TABLE(of, mtk_xfi_tphy_match);
++
++static struct platform_driver mtk_xfi_tphy_driver = {
++      .probe = mtk_xfi_tphy_probe,
++      .driver = {
++              .name = "mtk-xfi-tphy",
++              .of_match_table = mtk_xfi_tphy_match,
++      },
++};
++module_platform_driver(mtk_xfi_tphy_driver);
++
++MODULE_DESCRIPTION("MediaTek XFI T-PHY driver");
++MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
++MODULE_AUTHOR("Bc-bocun Chen <bc-bocun.chen@mediatek.com>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/pending-6.6/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch b/target/linux/generic/pending-6.6/739-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch
new file mode 100644 (file)
index 0000000..b67c8a0
--- /dev/null
@@ -0,0 +1,371 @@
+From 4b1a2716299c0e96a698044aebf3f80513509ae7 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 12 Dec 2023 03:47:18 +0000
+Subject: [PATCH 3/5] net: pcs: pcs-mtk-lynxi: add platform driver for MT7988
+
+Introduce a proper platform MFD driver for the LynxI (H)SGMII PCS which
+is going to initially be used for the MT7988 SoC.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/pcs/pcs-mtk-lynxi.c   | 227 ++++++++++++++++++++++++++++--
+ include/linux/pcs/pcs-mtk-lynxi.h |  11 ++
+ 2 files changed, 227 insertions(+), 11 deletions(-)
+
+--- a/drivers/net/pcs/pcs-mtk-lynxi.c
++++ b/drivers/net/pcs/pcs-mtk-lynxi.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0
+ // Copyright (c) 2018-2019 MediaTek Inc.
+-/* A library for MediaTek SGMII circuit
++/* A library and platform driver for the MediaTek LynxI SGMII circuit
+  *
+  * Author: Sean Wang <sean.wang@mediatek.com>
+  * Author: Alexander Couzens <lynxis@fe80.eu>
+@@ -8,11 +8,17 @@
+  *
+  */
++#include <linux/clk.h>
+ #include <linux/mdio.h>
++#include <linux/mfd/syscon.h>
++#include <linux/mutex.h>
+ #include <linux/of.h>
++#include <linux/of_platform.h>
+ #include <linux/pcs/pcs-mtk-lynxi.h>
+ #include <linux/phylink.h>
++#include <linux/platform_device.h>
+ #include <linux/regmap.h>
++#include <linux/reset.h>
+ /* SGMII subsystem config registers */
+ /* BMCR (low 16) BMSR (high 16) */
+@@ -65,6 +71,8 @@
+ #define SGMII_PN_SWAP_MASK            GENMASK(1, 0)
+ #define SGMII_PN_SWAP_TX_RX           (BIT(0) | BIT(1))
++#define MTK_NETSYS_V3_AMA_RGC3                0x128
++
+ /* struct mtk_pcs_lynxi -  This structure holds each sgmii regmap andassociated
+  *                         data
+  * @regmap:                The register map pointing at the range used to setup
+@@ -74,15 +82,29 @@
+  * @interface:             Currently configured interface mode
+  * @pcs:                   Phylink PCS structure
+  * @flags:                 Flags indicating hardware properties
++ * @rstc:                  Reset controller
++ * @sgmii_sel:             SGMII Register Clock
++ * @sgmii_rx:              SGMII RX Clock
++ * @sgmii_tx:              SGMII TX Clock
++ * @node:                  List node
+  */
+ struct mtk_pcs_lynxi {
+       struct regmap           *regmap;
++      struct device           *dev;
+       u32                     ana_rgc3;
+       phy_interface_t         interface;
+       struct                  phylink_pcs pcs;
+       u32                     flags;
++      struct reset_control    *rstc;
++      struct clk              *sgmii_sel;
++      struct clk              *sgmii_rx;
++      struct clk              *sgmii_tx;
++      struct list_head        node;
+ };
++static LIST_HEAD(mtk_pcs_lynxi_instances);
++static DEFINE_MUTEX(instance_mutex);
++
+ static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
+ {
+       return container_of(pcs, struct mtk_pcs_lynxi, pcs);
+@@ -102,6 +124,17 @@ static void mtk_pcs_lynxi_get_state(stru
+                                        FIELD_GET(SGMII_LPA, adv));
+ }
++static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs)
++{
++      if (!mpcs->rstc)
++              return;
++
++      reset_control_assert(mpcs->rstc);
++      udelay(100);
++      reset_control_deassert(mpcs->rstc);
++      mdelay(1);
++}
++
+ static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
+                               phy_interface_t interface,
+                               const unsigned long *advertising,
+@@ -147,6 +180,7 @@ static int mtk_pcs_lynxi_config(struct p
+                               SGMII_PHYA_PWD);
+               /* Reset SGMII PCS state */
++              mtk_sgmii_reset(mpcs);
+               regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
+                               SGMII_SW_RESET);
+@@ -233,10 +267,29 @@ static void mtk_pcs_lynxi_link_up(struct
+       }
+ }
++static int mtk_pcs_lynxi_enable(struct phylink_pcs *pcs)
++{
++      struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
++
++      if (mpcs->sgmii_tx && mpcs->sgmii_rx) {
++              clk_prepare_enable(mpcs->sgmii_rx);
++              clk_prepare_enable(mpcs->sgmii_tx);
++      }
++
++      return 0;
++}
++
+ static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs)
+ {
+       struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
++      regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
++
++      if (mpcs->sgmii_tx && mpcs->sgmii_rx) {
++              clk_disable_unprepare(mpcs->sgmii_tx);
++              clk_disable_unprepare(mpcs->sgmii_rx);
++      }
++
+       mpcs->interface = PHY_INTERFACE_MODE_NA;
+ }
+@@ -246,11 +299,12 @@ static const struct phylink_pcs_ops mtk_
+       .pcs_an_restart = mtk_pcs_lynxi_restart_an,
+       .pcs_link_up = mtk_pcs_lynxi_link_up,
+       .pcs_disable = mtk_pcs_lynxi_disable,
++      .pcs_enable = mtk_pcs_lynxi_enable,
+ };
+-struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
+-                                       struct regmap *regmap, u32 ana_rgc3,
+-                                       u32 flags)
++static struct phylink_pcs *mtk_pcs_lynxi_init(struct device *dev, struct regmap *regmap,
++                                            u32 ana_rgc3, u32 flags,
++                                            struct mtk_pcs_lynxi *prealloc)
+ {
+       struct mtk_pcs_lynxi *mpcs;
+       u32 id, ver;
+@@ -258,29 +312,33 @@ struct phylink_pcs *mtk_pcs_lynxi_create
+       ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
+       if (ret < 0)
+-              return NULL;
++              return ERR_PTR(ret);
+       if (id != SGMII_LYNXI_DEV_ID) {
+               dev_err(dev, "unknown PCS device id %08x\n", id);
+-              return NULL;
++              return ERR_PTR(-ENODEV);
+       }
+       ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
+       if (ret < 0)
+-              return NULL;
++              return ERR_PTR(ret);
+       ver = FIELD_GET(SGMII_DEV_VERSION, ver);
+       if (ver != 0x1) {
+               dev_err(dev, "unknown PCS device version %04x\n", ver);
+-              return NULL;
++              return ERR_PTR(-ENODEV);
+       }
+       dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
+               ver);
+-      mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
+-      if (!mpcs)
+-              return NULL;
++      if (prealloc) {
++              mpcs = prealloc;
++      } else {
++              mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
++              if (!mpcs)
++                      return ERR_PTR(-ENOMEM);
++      };
+       mpcs->ana_rgc3 = ana_rgc3;
+       mpcs->regmap = regmap;
+@@ -291,6 +349,13 @@ struct phylink_pcs *mtk_pcs_lynxi_create
+       mpcs->interface = PHY_INTERFACE_MODE_NA;
+       return &mpcs->pcs;
++};
++
++struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
++                                       struct regmap *regmap, u32 ana_rgc3,
++                                       u32 flags)
++{
++      return mtk_pcs_lynxi_init(dev, regmap, ana_rgc3, flags, NULL);
+ }
+ EXPORT_SYMBOL(mtk_pcs_lynxi_create);
+@@ -303,4 +368,144 @@ void mtk_pcs_lynxi_destroy(struct phylin
+ }
+ EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
++static int mtk_pcs_lynxi_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct device_node *np = dev->of_node;
++      struct mtk_pcs_lynxi *mpcs;
++      struct phylink_pcs *pcs;
++      struct regmap *regmap;
++      u32 flags = 0;
++
++      mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL);
++      if (!mpcs)
++              return -ENOMEM;
++
++      mpcs->dev = dev;
++      regmap = syscon_node_to_regmap(np->parent);
++      if (IS_ERR(regmap))
++              return PTR_ERR(regmap);
++
++      if (of_property_read_bool(np->parent, "mediatek,pnswap"))
++              flags |= MTK_SGMII_FLAG_PN_SWAP;
++
++      mpcs->rstc = of_reset_control_get_shared(np->parent, NULL);
++      if (IS_ERR(mpcs->rstc))
++              return PTR_ERR(mpcs->rstc);
++
++      reset_control_deassert(mpcs->rstc);
++      mpcs->sgmii_sel = devm_clk_get_enabled(dev, "sgmii_sel");
++      if (IS_ERR(mpcs->sgmii_sel))
++              return PTR_ERR(mpcs->sgmii_sel);
++
++      mpcs->sgmii_rx = devm_clk_get(dev, "sgmii_rx");
++      if (IS_ERR(mpcs->sgmii_rx))
++              return PTR_ERR(mpcs->sgmii_rx);
++
++      mpcs->sgmii_tx = devm_clk_get(dev, "sgmii_tx");
++      if (IS_ERR(mpcs->sgmii_tx))
++              return PTR_ERR(mpcs->sgmii_tx);
++
++      pcs = mtk_pcs_lynxi_init(dev, regmap, (uintptr_t)of_device_get_match_data(dev),
++                               flags, mpcs);
++      if (IS_ERR(pcs))
++              return PTR_ERR(pcs);
++
++      regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
++
++      platform_set_drvdata(pdev, mpcs);
++
++      mutex_lock(&instance_mutex);
++      list_add_tail(&mpcs->node, &mtk_pcs_lynxi_instances);
++      mutex_unlock(&instance_mutex);
++
++      return 0;
++}
++
++static int mtk_pcs_lynxi_remove(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct mtk_pcs_lynxi *cur, *tmp;
++
++      mutex_lock(&instance_mutex);
++      list_for_each_entry_safe(cur, tmp, &mtk_pcs_lynxi_instances, node)
++              if (cur->dev == dev) {
++                      list_del(&cur->node);
++                      kfree(cur);
++                      break;
++              }
++      mutex_unlock(&instance_mutex);
++
++      return 0;
++}
++
++static const struct of_device_id mtk_pcs_lynxi_of_match[] = {
++      { .compatible = "mediatek,mt7988-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 },
++      { /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, mtk_pcs_lynxi_of_match);
++
++struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np)
++{
++      struct platform_device *pdev;
++      struct mtk_pcs_lynxi *mpcs;
++
++      if (!np)
++              return NULL;
++
++      if (!of_device_is_available(np))
++              return ERR_PTR(-ENODEV);
++
++      if (!of_match_node(mtk_pcs_lynxi_of_match, np))
++              return ERR_PTR(-EINVAL);
++
++      pdev = of_find_device_by_node(np);
++      if (!pdev || !platform_get_drvdata(pdev)) {
++              if (pdev)
++                      put_device(&pdev->dev);
++              return ERR_PTR(-EPROBE_DEFER);
++      }
++
++      mpcs = platform_get_drvdata(pdev);
++      device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
++
++      return &mpcs->pcs;
++}
++EXPORT_SYMBOL(mtk_pcs_lynxi_get);
++
++void mtk_pcs_lynxi_put(struct phylink_pcs *pcs)
++{
++      struct mtk_pcs_lynxi *cur, *mpcs = NULL;
++
++      if (!pcs)
++              return;
++
++      mutex_lock(&instance_mutex);
++      list_for_each_entry(cur, &mtk_pcs_lynxi_instances, node)
++              if (pcs == &cur->pcs) {
++                      mpcs = cur;
++                      break;
++              }
++      mutex_unlock(&instance_mutex);
++
++      if (WARN_ON(!mpcs))
++              return;
++
++      put_device(mpcs->dev);
++}
++EXPORT_SYMBOL(mtk_pcs_lynxi_put);
++
++static struct platform_driver mtk_pcs_lynxi_driver = {
++      .driver = {
++              .name                   = "mtk-pcs-lynxi",
++              .suppress_bind_attrs    = true,
++              .of_match_table         = mtk_pcs_lynxi_of_match,
++      },
++      .probe = mtk_pcs_lynxi_probe,
++      .remove = mtk_pcs_lynxi_remove,
++};
++module_platform_driver(mtk_pcs_lynxi_driver);
++
++MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
++MODULE_DESCRIPTION("MediaTek LynxI HSGMII PCS");
+ MODULE_LICENSE("GPL");
+--- a/include/linux/pcs/pcs-mtk-lynxi.h
++++ b/include/linux/pcs/pcs-mtk-lynxi.h
+@@ -10,4 +10,15 @@ struct phylink_pcs *mtk_pcs_lynxi_create
+                                        struct regmap *regmap,
+                                        u32 ana_rgc3, u32 flags);
+ void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
++
++#if IS_ENABLED(CONFIG_PCS_MTK_LYNXI)
++struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np);
++void mtk_pcs_lynxi_put(struct phylink_pcs *pcs);
++#else
++static inline struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np)
++{
++      return NULL;
++}
++static inline void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) { }
++#endif /* IS_ENABLED(CONFIG_PCS_MTK_LYNXI) */
+ #endif
diff --git a/target/linux/generic/pending-6.6/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch b/target/linux/generic/pending-6.6/739-04-dt-bindings-net-pcs-add-bindings-for-MediaTek-USXGMI.patch
new file mode 100644 (file)
index 0000000..215bd2c
--- /dev/null
@@ -0,0 +1,81 @@
+From 7d88d79c0f65b27a92754d7547f7af098b3de67b Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 12 Dec 2023 03:47:31 +0000
+Subject: [PATCH 4/5] dt-bindings: net: pcs: add bindings for MediaTek USXGMII
+ PCS
+
+MediaTek's USXGMII can be found in the MT7988 SoC. We need to access
+it in order to configure and monitor the Ethernet SerDes link in
+USXGMII, 10GBase-R and 5GBase-R mode. By including a wrapped
+legacy 1000Base-X/2500Base-X/Cisco SGMII LynxI PCS as well, those
+interface modes are also available.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ .../bindings/net/pcs/mediatek,usxgmii.yaml    | 60 +++++++++++++++++++
+ 1 file changed, 60 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/pcs/mediatek,usxgmii.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/pcs/mediatek,usxgmii.yaml
+@@ -0,0 +1,60 @@
++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/pcs/mediatek,usxgmii.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: MediaTek USXGMII PCS
++
++maintainers:
++  - Daniel Golle <daniel@makrotopia.org>
++
++description:
++  The MediaTek USXGMII PCS provides physical link control and status
++  for USXGMII, 10GBase-R and 5GBase-R links on the SerDes interfaces
++  provided by the PEXTP PHY.
++  In order to also support legacy 2500Base-X, 1000Base-X and Cisco
++  SGMII an existing mediatek,*-sgmiisys LynxI PCS is wrapped to
++  provide those interfaces modes on the same SerDes interfaces shared
++  with the USXGMII PCS.
++
++properties:
++  $nodename:
++    pattern: "^pcs@[0-9a-f]+$"
++
++  compatible:
++    const: mediatek,mt7988-usxgmiisys
++
++  reg:
++    maxItems: 1
++
++  clocks:
++    items:
++      - description: USXGMII top-level clock
++
++  resets:
++    items:
++      - description: XFI reset
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - resets
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/mediatek,mt7988-clk.h>
++    #define MT7988_TOPRGU_XFI0_GRST 12
++    soc {
++      #address-cells = <2>;
++      #size-cells = <2>;
++        usxgmiisys0: pcs@10080000 {
++          compatible = "mediatek,mt7988-usxgmiisys";
++          reg = <0 0x10080000 0 0x1000>;
++          clocks = <&topckgen CLK_TOP_USXGMII_SBUS_0_SEL>;
++          resets = <&watchdog MT7988_TOPRGU_XFI0_GRST>;
++        };
++    };
diff --git a/target/linux/generic/pending-6.6/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch b/target/linux/generic/pending-6.6/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch
new file mode 100644 (file)
index 0000000..c7fcac3
--- /dev/null
@@ -0,0 +1,547 @@
+From dde0e95fff92e9f5009f3bea75278e0e34a48822 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 12 Dec 2023 03:47:47 +0000
+Subject: [PATCH 5/5] net: pcs: add driver for MediaTek USXGMII PCS
+
+Add driver for USXGMII PCS found in the MediaTek MT7988 SoC and supporting
+USXGMII, 10GBase-R and 5GBase-R interface modes.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ MAINTAINERS                         |   2 +
+ drivers/net/pcs/Kconfig             |  11 +
+ drivers/net/pcs/Makefile            |   1 +
+ drivers/net/pcs/pcs-mtk-usxgmii.c   | 456 ++++++++++++++++++++++++++++
+ include/linux/pcs/pcs-mtk-usxgmii.h |  27 ++
+ 5 files changed, 497 insertions(+)
+ create mode 100644 drivers/net/pcs/pcs-mtk-usxgmii.c
+ create mode 100644 include/linux/pcs/pcs-mtk-usxgmii.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -13356,7 +13356,9 @@ M:     Daniel Golle <daniel@makrotopia.org>
+ L:    netdev@vger.kernel.org
+ S:    Maintained
+ F:    drivers/net/pcs/pcs-mtk-lynxi.c
++F:    drivers/net/pcs/pcs-mtk-usxgmii.c
+ F:    include/linux/pcs/pcs-mtk-lynxi.h
++F:    include/linux/pcs/pcs-mtk-usxgmii.h
+ MEDIATEK ETHERNET PHY DRIVERS
+ M:    Daniel Golle <daniel@makrotopia.org>
+--- a/drivers/net/pcs/Kconfig
++++ b/drivers/net/pcs/Kconfig
+@@ -25,6 +25,17 @@ config PCS_MTK_LYNXI
+         This module provides helpers to phylink for managing the LynxI PCS
+         which is part of MediaTek's SoC and Ethernet switch ICs.
++config PCS_MTK_USXGMII
++      tristate "MediaTek USXGMII PCS"
++      select PCS_MTK_LYNXI
++      select PHY_MTK_PEXTP
++      select PHYLINK
++      help
++        This module provides a driver for MediaTek's USXGMII PCS supporting
++        10GBase-R, 5GBase-R and USXGMII interface modes.
++        1000Base-X, 2500Base-X and Cisco SGMII are supported on the same
++        differential pairs via an embedded LynxI PHY.
++
+ config PCS_RZN1_MIIC
+       tristate "Renesas RZ/N1 MII converter"
+       depends on OF && (ARCH_RZN1 || COMPILE_TEST)
+--- a/drivers/net/pcs/Makefile
++++ b/drivers/net/pcs/Makefile
+@@ -7,3 +7,4 @@ obj-$(CONFIG_PCS_XPCS)         += pcs_xpcs.o
+ obj-$(CONFIG_PCS_LYNX)                += pcs-lynx.o
+ obj-$(CONFIG_PCS_MTK_LYNXI)   += pcs-mtk-lynxi.o
+ obj-$(CONFIG_PCS_RZN1_MIIC)   += pcs-rzn1-miic.o
++obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o
+--- /dev/null
++++ b/drivers/net/pcs/pcs-mtk-usxgmii.c
+@@ -0,0 +1,456 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2023 MediaTek Inc.
++ * Author: Henry Yen <henry.yen@mediatek.com>
++ *         Daniel Golle <daniel@makrotopia.org>
++ */
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/mfd/syscon.h>
++#include <linux/mdio.h>
++#include <linux/mutex.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/reset.h>
++#include <linux/pcs/pcs-mtk-usxgmii.h>
++#include <linux/platform_device.h>
++
++/* USXGMII subsystem config registers */
++/* Register to control speed */
++#define RG_PHY_TOP_SPEED_CTRL1                        0x80c
++#define USXGMII_RATE_UPDATE_MODE              BIT(31)
++#define USXGMII_MAC_CK_GATED                  BIT(29)
++#define USXGMII_IF_FORCE_EN                   BIT(28)
++#define USXGMII_RATE_ADAPT_MODE                       GENMASK(10, 8)
++#define USXGMII_RATE_ADAPT_MODE_X1            0
++#define USXGMII_RATE_ADAPT_MODE_X2            1
++#define USXGMII_RATE_ADAPT_MODE_X4            2
++#define USXGMII_RATE_ADAPT_MODE_X10           3
++#define USXGMII_RATE_ADAPT_MODE_X100          4
++#define USXGMII_RATE_ADAPT_MODE_X5            5
++#define USXGMII_RATE_ADAPT_MODE_X50           6
++#define USXGMII_XFI_RX_MODE                   GENMASK(6, 4)
++#define USXGMII_XFI_TX_MODE                   GENMASK(2, 0)
++#define USXGMII_XFI_MODE_10G                  0
++#define USXGMII_XFI_MODE_5G                   1
++#define USXGMII_XFI_MODE_2P5G                 3
++
++/* Register to control PCS AN */
++#define RG_PCS_AN_CTRL0                               0x810
++#define USXGMII_AN_RESTART                    BIT(31)
++#define USXGMII_AN_SYNC_CNT                   GENMASK(30, 11)
++#define USXGMII_AN_ENABLE                     BIT(0)
++
++#define RG_PCS_AN_CTRL2                               0x818
++#define USXGMII_LINK_TIMER_IDLE_DETECT                GENMASK(29, 20)
++#define USXGMII_LINK_TIMER_COMP_ACK_DETECT    GENMASK(19, 10)
++#define USXGMII_LINK_TIMER_AN_RESTART         GENMASK(9, 0)
++
++/* Register to read PCS AN status */
++#define RG_PCS_AN_STS0                                0x81c
++#define USXGMII_LPA                           GENMASK(15, 0)
++#define USXGMII_LPA_LATCH                     BIT(31)
++
++/* Register to read PCS link status */
++#define RG_PCS_RX_STATUS0                     0x904
++#define RG_PCS_RX_STATUS_UPDATE                       BIT(16)
++#define RG_PCS_RX_LINK_STATUS                 BIT(2)
++
++/* struct mtk_usxgmii_pcs - This structure holds each usxgmii PCS
++ * @pcs:              Phylink PCS structure
++ * @dev:              Pointer to device structure
++ * @base:             IO memory to access PCS hardware
++ * @clk:              Pointer to USXGMII clk
++ * @reset:            Pointer to USXGMII reset control
++ * @interface:                Currently selected interface mode
++ * @neg_mode:         Currently used phylink neg_mode
++ * @node:             List node
++ */
++struct mtk_usxgmii_pcs {
++      struct phylink_pcs              pcs;
++      struct device                   *dev;
++      void __iomem                    *base;
++      struct clk                      *clk;
++      struct reset_control            *reset;
++      phy_interface_t                 interface;
++      unsigned int                    neg_mode;
++      struct list_head                node;
++};
++
++static LIST_HEAD(mtk_usxgmii_pcs_instances);
++static DEFINE_MUTEX(instance_mutex);
++
++static u32 mtk_r32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg)
++{
++      return ioread32(mpcs->base + reg);
++}
++
++static void mtk_m32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg, u32 mask, u32 set)
++{
++      u32 val;
++
++      val = ioread32(mpcs->base + reg);
++      val &= ~mask;
++      val |= set;
++      iowrite32(val, mpcs->base + reg);
++}
++
++static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs)
++{
++      return container_of(pcs, struct mtk_usxgmii_pcs, pcs);
++}
++
++static void mtk_usxgmii_reset(struct mtk_usxgmii_pcs *mpcs)
++{
++      reset_control_assert(mpcs->reset);
++      udelay(100);
++      reset_control_deassert(mpcs->reset);
++
++      mdelay(10);
++}
++
++static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
++                                phy_interface_t interface,
++                                const unsigned long *advertising,
++                                bool permit_pause_to_mac)
++{
++      struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
++      unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0;
++      bool mode_changed = false;
++
++      if (interface == PHY_INTERFACE_MODE_USXGMII) {
++              an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) | USXGMII_AN_ENABLE;
++              link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) |
++                           FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) |
++                           FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B);
++              xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) |
++                         FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G);
++      } else if (interface == PHY_INTERFACE_MODE_10GBASER) {
++              an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF);
++              link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) |
++                           FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) |
++                           FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B);
++              xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) |
++                         FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G);
++              adapt_mode = USXGMII_RATE_UPDATE_MODE;
++      } else if (interface == PHY_INTERFACE_MODE_5GBASER) {
++              an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF);
++              link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) |
++                           FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) |
++                           FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D);
++              xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_5G) |
++                         FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_5G);
++              adapt_mode = USXGMII_RATE_UPDATE_MODE;
++      } else {
++              return -EINVAL;
++      }
++
++      adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1);
++
++      if (mpcs->interface != interface) {
++              mpcs->interface = interface;
++              mode_changed = true;
++      }
++
++      mtk_usxgmii_reset(mpcs);
++
++      /* Setup USXGMII AN ctrl */
++      mtk_m32(mpcs, RG_PCS_AN_CTRL0,
++              USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE,
++              an_ctrl);
++
++      mtk_m32(mpcs, RG_PCS_AN_CTRL2,
++              USXGMII_LINK_TIMER_IDLE_DETECT |
++              USXGMII_LINK_TIMER_COMP_ACK_DETECT |
++              USXGMII_LINK_TIMER_AN_RESTART,
++              link_timer);
++
++      mpcs->neg_mode = neg_mode;
++
++      /* Gated MAC CK */
++      mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1,
++              USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED);
++
++      /* Enable interface force mode */
++      mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1,
++              USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN);
++
++      /* Setup USXGMII adapt mode */
++      mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1,
++              USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE,
++              adapt_mode);
++
++      /* Setup USXGMII speed */
++      mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1,
++              USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE,
++              xfi_mode);
++
++      usleep_range(1, 10);
++
++      /* Un-gated MAC CK */
++      mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_MAC_CK_GATED, 0);
++
++      usleep_range(1, 10);
++
++      /* Disable interface force mode for the AN mode */
++      if (an_ctrl & USXGMII_AN_ENABLE)
++              mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_IF_FORCE_EN, 0);
++
++      return mode_changed;
++}
++
++static void mtk_usxgmii_pcs_get_fixed_speed(struct mtk_usxgmii_pcs *mpcs,
++                                          struct phylink_link_state *state)
++{
++      u32 val = mtk_r32(mpcs, RG_PHY_TOP_SPEED_CTRL1);
++      int speed;
++
++      /* Calculate speed from interface speed and rate adapt mode */
++      switch (FIELD_GET(USXGMII_XFI_RX_MODE, val)) {
++      case USXGMII_XFI_MODE_10G:
++              speed = 10000;
++              break;
++      case USXGMII_XFI_MODE_5G:
++              speed = 5000;
++              break;
++      case USXGMII_XFI_MODE_2P5G:
++              speed = 2500;
++              break;
++      default:
++              state->speed = SPEED_UNKNOWN;
++              return;
++      }
++
++      switch (FIELD_GET(USXGMII_RATE_ADAPT_MODE, val)) {
++      case USXGMII_RATE_ADAPT_MODE_X100:
++              speed /= 100;
++              break;
++      case USXGMII_RATE_ADAPT_MODE_X50:
++              speed /= 50;
++              break;
++      case USXGMII_RATE_ADAPT_MODE_X10:
++              speed /= 10;
++              break;
++      case USXGMII_RATE_ADAPT_MODE_X5:
++              speed /= 5;
++              break;
++      case USXGMII_RATE_ADAPT_MODE_X4:
++              speed /= 4;
++              break;
++      case USXGMII_RATE_ADAPT_MODE_X2:
++              speed /= 2;
++              break;
++      case USXGMII_RATE_ADAPT_MODE_X1:
++              break;
++      default:
++              state->speed = SPEED_UNKNOWN;
++              return;
++      }
++
++      state->speed = speed;
++      state->duplex = DUPLEX_FULL;
++}
++
++static void mtk_usxgmii_pcs_get_an_state(struct mtk_usxgmii_pcs *mpcs,
++                                       struct phylink_link_state *state)
++{
++      u16 lpa;
++
++      /* Refresh LPA by toggling LPA_LATCH */
++      mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, USXGMII_LPA_LATCH);
++      ndelay(1020);
++      mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, 0);
++      ndelay(1020);
++      lpa = FIELD_GET(USXGMII_LPA, mtk_r32(mpcs, RG_PCS_AN_STS0));
++
++      phylink_decode_usxgmii_word(state, lpa);
++}
++
++static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs,
++                                    struct phylink_link_state *state)
++{
++      struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
++
++      /* Refresh USXGMII link status by toggling RG_PCS_AN_STATUS_UPDATE */
++      mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE,
++              RG_PCS_RX_STATUS_UPDATE);
++      ndelay(1020);
++      mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE, 0);
++      ndelay(1020);
++
++      /* Read USXGMII link status */
++      state->link = FIELD_GET(RG_PCS_RX_LINK_STATUS,
++                              mtk_r32(mpcs, RG_PCS_RX_STATUS0));
++
++      /* Continuously repeat re-configuration sequence until link comes up */
++      if (!state->link) {
++              mtk_usxgmii_pcs_config(pcs, mpcs->neg_mode,
++                                     state->interface, NULL, false);
++              return;
++      }
++
++      if (FIELD_GET(USXGMII_AN_ENABLE, mtk_r32(mpcs, RG_PCS_AN_CTRL0)))
++              mtk_usxgmii_pcs_get_an_state(mpcs, state);
++      else
++              mtk_usxgmii_pcs_get_fixed_speed(mpcs, state);
++}
++
++static void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs)
++{
++      struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
++
++      mtk_m32(mpcs, RG_PCS_AN_CTRL0, USXGMII_AN_RESTART, USXGMII_AN_RESTART);
++}
++
++static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
++                                  phy_interface_t interface,
++                                  int speed, int duplex)
++{
++      /* Reconfiguring USXGMII to ensure the quality of the RX signal
++       * after the line side link up.
++       */
++      mtk_usxgmii_pcs_config(pcs, neg_mode, interface, NULL, false);
++}
++
++static void mtk_usxgmii_pcs_disable(struct phylink_pcs *pcs)
++{
++      struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
++
++      mpcs->interface = PHY_INTERFACE_MODE_NA;
++      mpcs->neg_mode = -1;
++}
++
++static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = {
++      .pcs_config = mtk_usxgmii_pcs_config,
++      .pcs_get_state = mtk_usxgmii_pcs_get_state,
++      .pcs_an_restart = mtk_usxgmii_pcs_restart_an,
++      .pcs_link_up = mtk_usxgmii_pcs_link_up,
++      .pcs_disable = mtk_usxgmii_pcs_disable,
++};
++
++static int mtk_usxgmii_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct mtk_usxgmii_pcs *mpcs;
++
++      mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL);
++      if (!mpcs)
++              return -ENOMEM;
++
++      mpcs->base = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(mpcs->base))
++              return PTR_ERR(mpcs->base);
++
++      mpcs->dev = dev;
++      mpcs->pcs.ops = &mtk_usxgmii_pcs_ops;
++      mpcs->pcs.poll = true;
++      mpcs->pcs.neg_mode = true;
++      mpcs->interface = PHY_INTERFACE_MODE_NA;
++      mpcs->neg_mode = -1;
++
++      mpcs->clk = devm_clk_get_enabled(mpcs->dev, NULL);
++      if (IS_ERR(mpcs->clk))
++              return PTR_ERR(mpcs->clk);
++
++      mpcs->reset = devm_reset_control_get_shared(dev, NULL);
++      if (IS_ERR(mpcs->reset))
++              return PTR_ERR(mpcs->reset);
++
++      reset_control_deassert(mpcs->reset);
++
++      platform_set_drvdata(pdev, mpcs);
++
++      mutex_lock(&instance_mutex);
++      list_add_tail(&mpcs->node, &mtk_usxgmii_pcs_instances);
++      mutex_unlock(&instance_mutex);
++
++      return 0;
++}
++
++static int mtk_usxgmii_remove(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct mtk_usxgmii_pcs *cur, *tmp;
++
++      mutex_lock(&instance_mutex);
++      list_for_each_entry_safe(cur, tmp, &mtk_usxgmii_pcs_instances, node)
++              if (cur->dev == dev) {
++                      list_del(&cur->node);
++                      break;
++              }
++      mutex_unlock(&instance_mutex);
++
++      return 0;
++}
++
++static const struct of_device_id mtk_usxgmii_of_mtable[] = {
++      { .compatible = "mediatek,mt7988-usxgmiisys" },
++      { /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, mtk_usxgmii_of_mtable);
++
++struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np)
++{
++      struct platform_device *pdev;
++      struct mtk_usxgmii_pcs *mpcs;
++
++      if (!np)
++              return NULL;
++
++      if (!of_device_is_available(np))
++              return ERR_PTR(-ENODEV);
++
++      if (!of_match_node(mtk_usxgmii_of_mtable, np))
++              return ERR_PTR(-EINVAL);
++
++      pdev = of_find_device_by_node(np);
++      if (!pdev || !platform_get_drvdata(pdev)) {
++              if (pdev)
++                      put_device(&pdev->dev);
++              return ERR_PTR(-EPROBE_DEFER);
++      }
++
++      mpcs = platform_get_drvdata(pdev);
++      device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
++
++      return &mpcs->pcs;
++}
++EXPORT_SYMBOL(mtk_usxgmii_pcs_get);
++
++void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs)
++{
++      struct mtk_usxgmii_pcs *cur, *mpcs = NULL;
++
++      if (!pcs)
++              return;
++
++      mutex_lock(&instance_mutex);
++      list_for_each_entry(cur, &mtk_usxgmii_pcs_instances, node)
++              if (pcs == &cur->pcs) {
++                      mpcs = cur;
++                      break;
++              }
++      mutex_unlock(&instance_mutex);
++
++      if (WARN_ON(!mpcs))
++              return;
++
++      put_device(mpcs->dev);
++}
++EXPORT_SYMBOL(mtk_usxgmii_pcs_put);
++
++static struct platform_driver mtk_usxgmii_driver = {
++      .driver = {
++              .name                   = "mtk_usxgmii",
++              .suppress_bind_attrs    = true,
++              .of_match_table         = mtk_usxgmii_of_mtable,
++      },
++      .probe = mtk_usxgmii_probe,
++      .remove = mtk_usxgmii_remove,
++};
++module_platform_driver(mtk_usxgmii_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("MediaTek USXGMII PCS driver");
++MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+--- /dev/null
++++ b/include/linux/pcs/pcs-mtk-usxgmii.h
+@@ -0,0 +1,27 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef __LINUX_PCS_MTK_USXGMII_H
++#define __LINUX_PCS_MTK_USXGMII_H
++
++#include <linux/phylink.h>
++
++/**
++ * mtk_usxgmii_select_pcs() - Get MediaTek PCS instance
++ * @np:               Pointer to device node indentifying a MediaTek USXGMII PCS
++ * @mode:     Ethernet PHY interface mode
++ *
++ * Return PCS identified by a device node and the PHY interface mode in use
++ *
++ * Return:    Pointer to phylink PCS instance of NULL
++ */
++#if IS_ENABLED(CONFIG_PCS_MTK_USXGMII)
++struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np);
++void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs);
++#else
++static inline struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np)
++{
++      return NULL;
++}
++static inline void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) { }
++#endif /* IS_ENABLED(CONFIG_PCS_MTK_USXGMII) */
++
++#endif /* __LINUX_PCS_MTK_USXGMII_H */
diff --git a/target/linux/generic/pending-6.6/740-net-phy-motorcomm-Add-missing-include.patch b/target/linux/generic/pending-6.6/740-net-phy-motorcomm-Add-missing-include.patch
new file mode 100644 (file)
index 0000000..2a1f908
--- /dev/null
@@ -0,0 +1,22 @@
+From 6f291aa7da199c6486cc229b055dcbcd5cee7a21 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 21 May 2023 22:24:56 +0200
+Subject: [PATCH] net: phy: motorcomm: Add missing include
+
+Directly include linux/bitfield.h which provides FIELD_PREP.
+
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+---
+ drivers/net/phy/motorcomm.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -6,6 +6,7 @@
+  * Author: Frank <Frank.Sae@motor-comm.com>
+  */
++#include <linux/bitfield.h>
+ #include <linux/etherdevice.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
diff --git a/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch b/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch
new file mode 100644 (file)
index 0000000..80b6992
--- /dev/null
@@ -0,0 +1,35 @@
+From 1be3688b3eaa7ea2d9e19bd29ae6a6a51c121a0b Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Sat, 16 Nov 2024 22:36:15 +0100
+Subject: [PATCH] net: phy: broadcom: update dependency condition
+
+The broadcom PHY driver only has to depend upon PTP_1588_CLOCK_OPTIONAL
+if NETWORK_PHY_TIMESTAMPING is enabled. The PTP functionality is stubbed
+in this case.
+
+Reflect this circumstance in the dependence condition. This allows to
+build the driver as a built-in module even if PTP is built as a module.
+
+This is required to include the broadcom PHY module regardless of the
+built-setting of the PTP subsystem. On ath79 (and probably more)
+targets with Broadcom PHY, Gigabit operation is currently broken as the
+PHY driver is only built as a module in case all kernel-packages are
+built. Due to this circumstance, affected devices fall back to using the
+generic PHY driver.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -113,7 +113,7 @@ config BROADCOM_PHY
+       tristate "Broadcom 54XX PHYs"
+       select BCM_NET_PHYLIB
+       select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING
+-      depends on PTP_1588_CLOCK_OPTIONAL
++      depends on NETWORK_PHY_TIMESTAMPING=n || PTP_1588_CLOCK_OPTIONAL
+       help
+         Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464,
+         BCM5481, BCM54810 and BCM5482 PHYs.
diff --git a/target/linux/generic/pending-6.6/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch b/target/linux/generic/pending-6.6/742-net-phy-air_en8811h-reset-netdev-rules-when-LED-is-s.patch
new file mode 100644 (file)
index 0000000..500567b
--- /dev/null
@@ -0,0 +1,45 @@
+From 9be9a00adfac8118b6d685e71696f83187308c66 Mon Sep 17 00:00:00 2001
+Message-ID: <9be9a00adfac8118b6d685e71696f83187308c66.1715125851.git.daniel@makrotopia.org>
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Tue, 7 May 2024 22:43:30 +0100
+Subject: [PATCH net] net: phy: air_en8811h: reset netdev rules when LED is set
+ manually
+To: Andrew Lunn <andrew@lunn.ch>,
+    Heiner Kallweit <hkallweit1@gmail.com>,
+    Russell King <linux@armlinux.org.uk>,
+    David S. Miller <davem@davemloft.net>,
+    Eric Dumazet <edumazet@google.com>,
+    Jakub Kicinski <kuba@kernel.org>,
+    Paolo Abeni <pabeni@redhat.com>,
+    SkyLake Huang <skylake.huang@mediatek.com>,
+    Eric Woudstra <ericwouds@gmail.com>,
+    netdev@vger.kernel.org,
+    linux-kernel@vger.kernel.org
+
+Setting LED_OFF via the brightness_set should deactivate hw control,
+so make sure netdev trigger rules also get cleared in that case.
+
+Fixes: 71e79430117d ("net: phy: air_en8811h: Add the Airoha EN8811H PHY driver")
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+This is basically a stop-gap measure until unified LED handling has
+been implemented accross all MediaTek and Airoha PHYs.
+See also
+https://patchwork.kernel.org/project/netdevbpf/patch/20240425023325.15586-3-SkyLake.Huang@mediatek.com/
+
+ drivers/net/phy/air_en8811h.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/net/phy/air_en8811h.c
++++ b/drivers/net/phy/air_en8811h.c
+@@ -544,6 +544,10 @@ static int air_hw_led_on_set(struct phy_
+       changed |= (priv->led[index].rules != 0);
++      /* clear netdev trigger rules in case LED_OFF has been set */
++      if (!on)
++              priv->led[index].rules = 0;
++
+       if (changed)
+               return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
+                                     AIR_PHY_LED_ON(index),
diff --git a/target/linux/generic/pending-6.6/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/target/linux/generic/pending-6.6/780-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch
new file mode 100644 (file)
index 0000000..39ba716
--- /dev/null
@@ -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
+@@ -11,6 +11,7 @@
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/mbus.h>
++#include <linux/if_ether.h>
+ #include <linux/of.h>
+ #include <linux/of_address.h>
+ #include <linux/of_net.h>
diff --git a/target/linux/generic/pending-6.6/790-bus-mhi-core-add-SBL-state-callback.patch b/target/linux/generic/pending-6.6/790-bus-mhi-core-add-SBL-state-callback.patch
new file mode 100644 (file)
index 0000000..fe0f260
--- /dev/null
@@ -0,0 +1,48 @@
+From 5f7c5e1c0d7a79be144e5efc1f24728ddd7fc25c Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Sat, 5 Nov 2022 20:02:56 +0100
+Subject: [PATCH 1/2] bus: mhi: core: add SBL state callback
+
+Add support for SBL state callback in MHI core.
+
+It is required for ath11k MHI devices in order to be able to set QRTR
+instance ID in the SBL state so that QRTR instance ID-s dont conflict in
+case of multiple PCI/MHI cards or AHB + PCI/MHI card.
+Setting QRTR instance ID is only possible in SBL state and there is
+currently no way to ensure that we are in that state, so provide a
+callback that the controller can trigger off.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+---
+ drivers/bus/mhi/host/main.c | 1 +
+ include/linux/mhi.h         | 2 ++
+ 2 files changed, 3 insertions(+)
+
+--- a/drivers/bus/mhi/host/main.c
++++ b/drivers/bus/mhi/host/main.c
+@@ -906,6 +906,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_
+                       switch (event) {
+                       case MHI_EE_SBL:
+                               st = DEV_ST_TRANSITION_SBL;
++                              mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_SBL_MODE);
+                               break;
+                       case MHI_EE_WFW:
+                       case MHI_EE_AMSS:
+--- a/include/linux/mhi.h
++++ b/include/linux/mhi.h
+@@ -34,6 +34,7 @@ struct mhi_buf_info;
+  * @MHI_CB_SYS_ERROR: MHI device entered error state (may recover)
+  * @MHI_CB_FATAL_ERROR: MHI device entered fatal error state
+  * @MHI_CB_BW_REQ: Received a bandwidth switch request from device
++ * @MHI_CB_EE_SBL_MODE: MHI device entered SBL mode
+  */
+ enum mhi_callback {
+       MHI_CB_IDLE,
+@@ -45,6 +46,7 @@ enum mhi_callback {
+       MHI_CB_SYS_ERROR,
+       MHI_CB_FATAL_ERROR,
+       MHI_CB_BW_REQ,
++      MHI_CB_EE_SBL_MODE,
+ };
+ /**
diff --git a/target/linux/generic/pending-6.6/791-tg3-Fix-DMA-allocations-on-57766-devices.patch b/target/linux/generic/pending-6.6/791-tg3-Fix-DMA-allocations-on-57766-devices.patch
new file mode 100644 (file)
index 0000000..a90d77c
--- /dev/null
@@ -0,0 +1,31 @@
+From f992b15965177e2f280fb6f41f292214f9a6f8d5 Mon Sep 17 00:00:00 2001
+From: Pavan Chebbi <pavan.chebbi@broadcom.com>
+Date: Tue, 10 Dec 2024 03:28:31 -0800
+Subject: [PATCH] tg3: Fix DMA allocations on 57766 devices
+
+The coherent DMA mask of 31b may not be accepted if
+the DMA mask is configured to use higher memories of
+64b. Set the DMA mask also to lower 32b for 57766
+devices.
+
+Fixes: 614f4d166eee ("tg3: Set coherent DMA mask bits to 31 for BCM57766 chipsets")
+Reported-By: Rui Salvaterra <rsalvaterra@gmail.com>
+Signed-off-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
+---
+ drivers/net/ethernet/broadcom/tg3.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/broadcom/tg3.c
++++ b/drivers/net/ethernet/broadcom/tg3.c
+@@ -17731,8 +17731,10 @@ static int tg3_init_one(struct pci_dev *
+       } else
+               persist_dma_mask = dma_mask = DMA_BIT_MASK(64);
+-      if (tg3_asic_rev(tp) == ASIC_REV_57766)
++      if (tg3_asic_rev(tp) == ASIC_REV_57766) {
++              dma_mask = DMA_BIT_MASK(32);
+               persist_dma_mask = DMA_BIT_MASK(31);
++      }
+       /* Configure DMA attributes. */
+       if (dma_mask > DMA_BIT_MASK(32)) {
diff --git a/target/linux/generic/pending-6.6/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/target/linux/generic/pending-6.6/800-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch
new file mode 100644 (file)
index 0000000..4a9c188
--- /dev/null
@@ -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
+@@ -237,13 +237,17 @@ EXPORT_SYMBOL(bcma_core_irq);
+ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
+ {
+-      device_initialize(&core->dev);
++      struct device *dev = &core->dev;
++
++      device_initialize(dev);
+       core->dev.release = bcma_release_core_dev;
+       core->dev.bus = &bcma_bus_type;
+-      dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
++      dev_set_name(dev, "bcma%d:%d", bus->num, core->core_index);
+       core->dev.parent = bus->dev;
+-      if (bus->dev)
++      if (bus->dev) {
+               bcma_of_fill_device(bus->dev, core);
++              dma_coerce_mask_and_coherent(dev, bus->dev->coherent_dma_mask);
++      }
+       switch (bus->hosttype) {
+       case BCMA_HOSTTYPE_PCI:
diff --git a/target/linux/generic/pending-6.6/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch b/target/linux/generic/pending-6.6/801-gpio-gpio-cascade-add-generic-GPIO-cascade.patch
new file mode 100644 (file)
index 0000000..001be81
--- /dev/null
@@ -0,0 +1,217 @@
+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
+@@ -1820,4 +1820,19 @@ config GPIO_SIM
+ 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
+@@ -44,6 +44,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,112 @@
++// 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 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 = gpiochip_get_data(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, cas);
++}
++
++static const struct of_device_id gpio_cascade_id[] = {
++      { .compatible = "gpio-cascade" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, gpio_cascade_id);
++
++static struct platform_driver gpio_cascade_driver = {
++      .driver = {
++              .name           = "gpio-cascade",
++              .of_match_table = gpio_cascade_id,
++      },
++      .probe  = gpio_cascade_probe,
++};
++module_platform_driver(gpio_cascade_driver);
++
++MODULE_AUTHOR("Mauri Sandberg <maukka@ext.kapsi.fi>");
++MODULE_DESCRIPTION("Generic GPIO cascade");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/pending-6.6/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch b/target/linux/generic/pending-6.6/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch
new file mode 100644 (file)
index 0000000..7f67091
--- /dev/null
@@ -0,0 +1,108 @@
+From fd59b838dd90452f61a17dc9e5ff175205003068 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 15 Sep 2022 18:49:43 +0200
+Subject: [PATCH] OPP: Provide old opp to config_clks on _set_opp
+
+With the target opp, also pass the old opp to config_clks function.
+This can be useful when a driver needs to take decision on what fequency
+to set based on what is the current frequency without using a
+clk_get_freq call.
+Update the only user of custom config_clks (tegra30 devfreq driver) to
+this new implementation.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/devfreq/tegra30-devfreq.c |  5 +++--
+ drivers/opp/core.c                | 11 ++++++-----
+ include/linux/pm_opp.h            | 11 ++++++-----
+ 3 files changed, 15 insertions(+), 12 deletions(-)
+
+--- a/drivers/devfreq/tegra30-devfreq.c
++++ b/drivers/devfreq/tegra30-devfreq.c
+@@ -823,8 +823,9 @@ static int devm_tegra_devfreq_init_hw(st
+ static int tegra_devfreq_config_clks_nop(struct device *dev,
+                                        struct opp_table *opp_table,
+-                                       struct dev_pm_opp *opp, void *data,
+-                                       bool scaling_down)
++                                       struct dev_pm_opp *old_opp,
++                                       struct dev_pm_opp *opp,
++                                       void *data, bool scaling_down)
+ {
+       /* We want to skip clk configuration via dev_pm_opp_set_opp() */
+       return 0;
+--- a/drivers/opp/core.c
++++ b/drivers/opp/core.c
+@@ -902,7 +902,8 @@ static int _set_opp_voltage(struct devic
+ static int
+ _opp_config_clk_single(struct device *dev, struct opp_table *opp_table,
+-                     struct dev_pm_opp *opp, void *data, bool scaling_down)
++                     struct dev_pm_opp *old_opp, struct dev_pm_opp *opp,
++                     void *data, bool scaling_down)
+ {
+       unsigned long *target = data;
+       unsigned long freq;
+@@ -934,8 +935,8 @@ _opp_config_clk_single(struct device *de
+  * the order in which they are present in the array while scaling up.
+  */
+ int dev_pm_opp_config_clks_simple(struct device *dev,
+-              struct opp_table *opp_table, struct dev_pm_opp *opp, void *data,
+-              bool scaling_down)
++              struct opp_table *opp_table, struct dev_pm_opp *old_opp,
++              struct dev_pm_opp *opp, void *data, bool scaling_down)
+ {
+       int ret, i;
+@@ -1217,7 +1218,7 @@ static int _set_opp(struct device *dev,
+       }
+       if (opp_table->config_clks) {
+-              ret = opp_table->config_clks(dev, opp_table, opp, clk_data, scaling_down);
++              ret = opp_table->config_clks(dev, opp_table, old_opp, opp, clk_data, scaling_down);
+               if (ret)
+                       return ret;
+       }
+@@ -1292,7 +1293,7 @@ int dev_pm_opp_set_rate(struct device *d
+                * equivalent to a clk_set_rate()
+                */
+               if (!_get_opp_count(opp_table)) {
+-                      ret = opp_table->config_clks(dev, opp_table, NULL,
++                      ret = opp_table->config_clks(dev, opp_table, NULL, NULL,
+                                                    &target_freq, false);
+                       goto put_opp_table;
+               }
+--- a/include/linux/pm_opp.h
++++ b/include/linux/pm_opp.h
+@@ -61,7 +61,8 @@ typedef int (*config_regulators_t)(struc
+                       struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp,
+                       struct regulator **regulators, unsigned int count);
+-typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table,
++typedef int (*config_clks_t)(struct device *dev,
++                      struct opp_table *opp_table, struct dev_pm_opp *old_opp,
+                       struct dev_pm_opp *opp, void *data, bool scaling_down);
+ /**
+@@ -172,8 +173,8 @@ int dev_pm_opp_set_config(struct device
+ int devm_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config);
+ void dev_pm_opp_clear_config(int token);
+ int dev_pm_opp_config_clks_simple(struct device *dev,
+-              struct opp_table *opp_table, struct dev_pm_opp *opp, void *data,
+-              bool scaling_down);
++              struct opp_table *opp_table, struct dev_pm_opp *old_opp,
++              struct dev_pm_opp *opp, void *data, bool scaling_down);
+ struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, struct opp_table *dst_table, struct dev_pm_opp *src_opp);
+ int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
+@@ -377,8 +378,8 @@ static inline int devm_pm_opp_set_config
+ static inline void dev_pm_opp_clear_config(int token) {}
+ static inline int dev_pm_opp_config_clks_simple(struct device *dev,
+-              struct opp_table *opp_table, struct dev_pm_opp *opp, void *data,
+-              bool scaling_down)
++              struct opp_table *opp_table, struct dev_pm_opp *old_opp,
++              struct dev_pm_opp *opp, void *data, bool scaling_down)
+ {
+       return -EOPNOTSUPP;
+ }
diff --git a/target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch b/target/linux/generic/pending-6.6/802-nvmem-u-boot-env-align-endianness-of-crc32-values.patch
new file mode 100644 (file)
index 0000000..29fe668
--- /dev/null
@@ -0,0 +1,45 @@
+From 0e71cac033bb7689c4dfa2e6814191337ef770f5 Mon Sep 17 00:00:00 2001
+From: INAGAKI Hiroshi <musashino.open@gmail.com>
+Date: Thu, 13 Oct 2022 00:51:33 +0900
+Subject: [PATCH] nvmem: layouts: u-boot-env: align endianness of crc32 values
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch fixes crc32 error on Big-Endianness system by conversion of
+calculated crc32 value.
+
+Little-Endianness system:
+
+  obtained crc32: Little
+calculated crc32: Little
+
+Big-Endianness system:
+
+  obtained crc32: Little
+calculated crc32: Big
+
+log (APRESIA ApresiaLightGS120GT-SS, RTL8382M, Big-Endianness):
+
+[    8.570000] u_boot_env 18001200.spi:flash@0:partitions:partition@c0000: Invalid calculated CRC32: 0x88cd6f09 (expected: 0x096fcd88)
+[    8.580000] u_boot_env: probe of 18001200.spi:flash@0:partitions:partition@c0000 failed with error -22
+
+Fixes: f955dc1445069 ("nvmem: add driver handling U-Boot environment variables")
+
+Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
+Acked-by: Rafał Miłecki <rafal@milecki.pl>
+Tested-by: Christian Lamparter <chunkeey@gmail.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+---
+
+--- a/drivers/nvmem/layouts/u-boot-env.c
++++ b/drivers/nvmem/layouts/u-boot-env.c
+@@ -148,7 +148,7 @@ int u_boot_env_parse(struct device *dev,
+       crc32_data_len = dev_size - crc32_data_offset;
+       data_len = dev_size - data_offset;
+-      calc = crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L;
++      calc = le32_to_cpu((__le32)crc32(~0, buf + crc32_data_offset, crc32_data_len) ^ ~0L);
+       if (calc != crc32) {
+               dev_err(dev, "Invalid calculated CRC32: 0x%08x (expected: 0x%08x)\n", calc, crc32);
+               err = -EINVAL;
diff --git a/target/linux/generic/pending-6.6/804-nvmem-core-support-mac-base-fixed-layout-cells.patch b/target/linux/generic/pending-6.6/804-nvmem-core-support-mac-base-fixed-layout-cells.patch
new file mode 100644 (file)
index 0000000..241ff77
--- /dev/null
@@ -0,0 +1,124 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 13 Jul 2023 18:29:19 +0200
+Subject: [PATCH] nvmem: core: support "mac-base" fixed layout cells
+
+Fixed layout binding allows specifying "mac-base" NVMEM cells. It's used
+for base MAC address (that can be used for calculating relative
+addresses). It can be stored in a raw binary format or as an ASCII
+string.
+---
+
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -2,6 +2,7 @@
+ menuconfig NVMEM
+       bool "NVMEM Support"
+       imply NVMEM_LAYOUTS
++      select GENERIC_NET_UTILS
+       help
+         Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -7,9 +7,12 @@
+  */
+ #include <linux/device.h>
++#include <linux/ctype.h>
++#include <linux/etherdevice.h>
+ #include <linux/export.h>
+ #include <linux/fs.h>
+ #include <linux/idr.h>
++#include <linux/if_ether.h>
+ #include <linux/init.h>
+ #include <linux/kref.h>
+ #include <linux/module.h>
+@@ -796,6 +799,62 @@ static int nvmem_validate_keepouts(struc
+       return 0;
+ }
++static int nvmem_mac_base_raw_read(void *context, const char *id, int index, unsigned int offset,
++                                 void *buf, size_t bytes)
++{
++      if (WARN_ON(bytes != ETH_ALEN))
++              return -EINVAL;
++
++      if (index)
++              eth_addr_add(buf, index);
++
++      return 0;
++}
++
++static int nvmem_mac_base_ascii_read(void *context, const char *id, int index, unsigned int offset,
++                                   void *buf, size_t bytes)
++{
++      u8 mac[ETH_ALEN];
++
++      if (WARN_ON(bytes != 3 * ETH_ALEN - 1))
++              return -EINVAL;
++
++      if (!mac_pton(buf, mac))
++              return -EINVAL;
++
++      if (index)
++              eth_addr_add(mac, index);
++
++      ether_addr_copy(buf, mac);
++
++      return 0;
++}
++
++static int nvmem_mac_base_hex_read(void *context, const char *id, int index, unsigned int offset,
++                                 void *buf, size_t bytes)
++{
++      u8 mac[ETH_ALEN], *hexstr;
++      int i;
++
++      if (WARN_ON(bytes != 2 * ETH_ALEN))
++              return -EINVAL;
++
++      hexstr = (u8 *)buf;
++      for (i = 0; i < ETH_ALEN; i++) {
++              if (!isxdigit(hexstr[i * 2]) || !isxdigit(hexstr[i * 2 + 1]))
++                      return -EINVAL;
++
++              mac[i] = (hex_to_bin(hexstr[i * 2]) << 4) | hex_to_bin(hexstr[i * 2 + 1]);
++      }
++
++      if (index)
++              eth_addr_add(mac, index);
++
++      ether_addr_copy(buf, mac);
++
++      return 0;
++}
++
+ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
+ {
+       struct device *dev = &nvmem->dev;
+@@ -835,6 +894,25 @@ static int nvmem_add_cells_from_dt(struc
+               if (nvmem->fixup_dt_cell_info)
+                       nvmem->fixup_dt_cell_info(nvmem, &info);
++              if (of_device_is_compatible(np, "fixed-layout")) {
++                      if (of_device_is_compatible(child, "mac-base")) {
++                              if (info.bytes == ETH_ALEN) {
++                                      info.raw_len = info.bytes;
++                                      info.bytes = ETH_ALEN;
++                                      info.read_post_process = nvmem_mac_base_raw_read;
++                              } else if (info.bytes == 2 * ETH_ALEN) {
++                                      info.raw_len = info.bytes;
++                                      info.bytes = ETH_ALEN;
++                                      info.read_post_process = nvmem_mac_base_hex_read;
++                              } else if (info.bytes == 3 * ETH_ALEN - 1) {
++                                      info.raw_len = info.bytes;
++                                      info.bytes = ETH_ALEN;
++                                      info.read_post_process = nvmem_mac_base_ascii_read;
++                              }
++
++                      }
++              }
++
+               ret = nvmem_add_one_cell(nvmem, &info);
+               kfree(info.name);
+               if (ret) {
diff --git a/target/linux/generic/pending-6.6/810-pci_disable_common_quirks.patch b/target/linux/generic/pending-6.6/810-pci_disable_common_quirks.patch
new file mode 100644 (file)
index 0000000..e4c8caf
--- /dev/null
@@ -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
+@@ -113,6 +113,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
+@@ -313,6 +313,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.
+@@ -3501,6 +3502,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
+@@ -3526,6 +3529,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
+@@ -3564,6 +3569,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq);
++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */
++
+ /*
+  * PCI devices which are on Intel chips can skip the 10ms delay
+  * before entering D3 mode.
diff --git a/target/linux/generic/pending-6.6/811-pci_disable_usb_common_quirks.patch b/target/linux/generic/pending-6.6/811-pci_disable_usb_common_quirks.patch
new file mode 100644 (file)
index 0000000..e91d1ef
--- /dev/null
@@ -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;
+@@ -631,6 +633,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.
+@@ -710,8 +716,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;
+@@ -1283,3 +1298,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
+@@ -485,7 +485,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
+ extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
+ #endif /* CONFIG_USB_PCI */
diff --git a/target/linux/generic/pending-6.6/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch b/target/linux/generic/pending-6.6/820-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch
new file mode 100644 (file)
index 0000000..33eb34c
--- /dev/null
@@ -0,0 +1,26 @@
+From d9c8bc8c1408f3e8529db6e4e04017b4c579c342 Mon Sep 17 00:00:00 2001
+From: Pawel Dembicki <paweldembicki@gmail.com>
+Date: Sun, 18 Feb 2018 17:08:04 +0100
+Subject: [PATCH] w1: gpio: fix problem with platfom data in w1-gpio
+
+In devices, where fdt is used, is impossible to apply platform data
+without proper fdt node.
+
+This patch allow to use platform data in devices with fdt.
+
+Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
+---
+ drivers/w1/masters/w1-gpio.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/drivers/w1/masters/w1-gpio.c
++++ b/drivers/w1/masters/w1-gpio.c
+@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform
+       enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
+       int err;
+-      if (of_have_populated_dt()) {
++      if (of_have_populated_dt() && !dev_get_platdata(&pdev->dev)) {
+               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+               if (!pdata)
+                       return -ENOMEM;
diff --git a/target/linux/generic/pending-6.6/834-ledtrig-libata.patch b/target/linux/generic/pending-6.6/834-ledtrig-libata.patch
new file mode 100644 (file)
index 0000000..672d0d5
--- /dev/null
@@ -0,0 +1,147 @@
+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
+@@ -685,6 +685,17 @@ static inline void ata_set_tf_cdl(struct
+       qc->flags |= ATA_QCFLAG_HAS_CDL | ATA_QCFLAG_RESULT_TF;
+ }
++#ifdef CONFIG_ATA_LEDS
++#define LIBATA_BLINK_DELAY 20 /* ms */
++static inline void ata_led_act(struct ata_port *ap)
++{
++      if (unlikely(!ap->ledtrig))
++              return;
++
++      led_trigger_blink_oneshot(ap->ledtrig, LIBATA_BLINK_DELAY, LIBATA_BLINK_DELAY, 0);
++}
++#endif
++
+ /**
+  *    ata_build_rw_tf - Build ATA taskfile for given read/write request
+  *    @qc: Metadata associated with the taskfile to build
+@@ -4771,6 +4782,9 @@ void __ata_qc_complete(struct ata_queued
+               link->active_tag = ATA_TAG_POISON;
+               ap->nr_active_links--;
+       }
++#ifdef CONFIG_ATA_LEDS
++      ata_led_act(ap);
++#endif
+       /* clear exclusive status */
+       if (unlikely(qc->flags & ATA_QCFLAG_CLEAR_EXCL &&
+@@ -5494,6 +5508,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;
+@@ -5507,6 +5524,12 @@ void ata_port_free(struct ata_port *ap)
+       kfree(ap->pmp_link);
+       kfree(ap->slave_link);
+       kfree(ap->ncq_sense_buf);
++#ifdef CONFIG_ATA_LEDS
++      if (ap->ledtrig) {
++              led_trigger_unregister(ap->ledtrig);
++              kfree(ap->ledtrig);
++      };
++#endif
+       kfree(ap);
+ }
+ EXPORT_SYMBOL_GPL(ata_port_free);
+@@ -5929,7 +5952,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
+@@ -875,6 +878,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                      *ncq_sense_buf;
+       u8                      sector_buf[ATA_SECT_SIZE] ____cacheline_aligned;
diff --git a/target/linux/generic/pending-6.6/840-hwrng-bcm2835-set-quality-to-1000.patch b/target/linux/generic/pending-6.6/840-hwrng-bcm2835-set-quality-to-1000.patch
new file mode 100644 (file)
index 0000000..3d3b034
--- /dev/null
@@ -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
+@@ -171,6 +171,7 @@ static int bcm2835_rng_probe(struct plat
+       priv->rng.init = bcm2835_rng_init;
+       priv->rng.read = bcm2835_rng_read;
+       priv->rng.cleanup = bcm2835_rng_cleanup;
++      priv->rng.quality = 1000;
+       if (dev_of_node(dev)) {
+               rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node);
diff --git a/target/linux/generic/pending-6.6/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/target/linux/generic/pending-6.6/850-0023-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch
new file mode 100644 (file)
index 0000000..fc61ee2
--- /dev/null
@@ -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
+@@ -277,7 +277,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;
+@@ -1426,14 +1425,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;
+@@ -1492,7 +1496,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);
+@@ -1503,28 +1506,14 @@ static int advk_pcie_init_irq_domain(str
+               return -ENODEV;
+       }
+-      irq_chip = &pcie->irq_chip;
+-
+-      irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq",
+-                                      dev_name(dev));
+-      if (!irq_chip->name) {
+-              ret = -ENOMEM;
+-              goto out_put_node;
+-      }
+-
+-      irq_chip->irq_mask = advk_pcie_irq_mask;
+-      irq_chip->irq_unmask = advk_pcie_irq_unmask;
+-
+       pcie->irq_domain =
+               irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
+                                     &advk_pcie_irq_domain_ops, pcie);
+       if (!pcie->irq_domain) {
+               dev_err(dev, "Failed to get a INTx IRQ domain\n");
+               ret = -ENOMEM;
+-              goto out_put_node;
+       }
+-out_put_node:
+       of_node_put(pcie_intc_node);
+       return ret;
+ }
diff --git a/target/linux/generic/pending-6.6/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch b/target/linux/generic/pending-6.6/870-ARM-dts-nxp-imx7d-pico-add-cpu-supply-nodes.patch
new file mode 100644 (file)
index 0000000..1f860e9
--- /dev/null
@@ -0,0 +1,43 @@
+From d0562705bcd4cb9849156f095b2af0ec1bb53b56 Mon Sep 17 00:00:00 2001
+From: Lech Perczak <lech.perczak@gmail.com>
+Date: Fri, 17 Nov 2023 21:33:04 +0100
+Subject: [PATCH] ARM: dts: nxp: imx7d-pico: add cpu-supply nodes
+
+The PICO-IMX7D SoM has the usual power supply configuration using
+output sw1a of PF3000 PMIC, which was defined in downstream derivative
+of linux-imx (see link) in the sources for "Android Things" devkit.
+It is required to support CPU frequency scaling.
+
+Map the respective "cpu-supply" nodes of each core to sw1a of the PMIC.
+
+Enabling them causes cpufreq-dt, and imx-thermal drivers to probe
+successfully, and CPU frequency scaling to function.
+
+Link: https://android.googlesource.com/platform/hardware/bsp/kernel/nxp/imx-v4.1/+/o-iot-preview-5/arch/arm/boot/dts/imx7d-pico.dtsi#849
+
+Cc: Fabio Estevam <festevam@gmail.com>
+Cc: Shawn Guo <shawnguo@kernel.org>
+Cc: Sascha Hauer <s.hauer@pengutronix.de>
+
+Signed-off-by: Lech Perczak <lech.perczak@gmail.com>
+---
+ arch/arm/boot/dts/imx7d-pico.dtsi | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/arch/arm/boot/dts/nxp/imx/imx7d-pico.dtsi
++++ b/arch/arm/boot/dts/nxp/imx/imx7d-pico.dtsi
+@@ -108,6 +108,14 @@
+       assigned-clock-rates = <0>, <32768>;
+ };
++&cpu0 {
++      cpu-supply = <&sw1a_reg>;
++};
++
++&cpu1 {
++      cpu-supply = <&sw1a_reg>;
++};
++
+ &ecspi3 {
+       cs-gpios = <&gpio4 11 GPIO_ACTIVE_LOW>;
+       pinctrl-names = "default";
diff --git a/target/linux/generic/pending-6.6/880-01-dt-bindings-leds-add-LED_FUNCTION_MOBILE-for-mobile-.patch b/target/linux/generic/pending-6.6/880-01-dt-bindings-leds-add-LED_FUNCTION_MOBILE-for-mobile-.patch
new file mode 100644 (file)
index 0000000..3321b03
--- /dev/null
@@ -0,0 +1,37 @@
+From 38eb5b3370c29515d2ce92adac2d6eba96f276f5 Mon Sep 17 00:00:00 2001
+From: INAGAKI Hiroshi <musashino.open@gmail.com>
+Date: Wed, 20 Mar 2024 15:32:18 +0900
+Subject: [PATCH v2 1/2] dt-bindings: leds: add LED_FUNCTION_MOBILE for mobile
+ network
+
+Add LED_FUNCTION_MOBILE for LEDs that indicate status of mobile network
+connection. This is useful to distinguish those LEDs from LEDs that
+indicates status of wired "wan" connection.
+
+example (on stock fw):
+
+IIJ SA-W2 has "Mobile" LEDs that indicate status (no signal, too low,
+low, good) of mobile network connection via dongle connected to USB
+port.
+
+- no signal: (none, turned off)
+-   too low: green:mobile & red:mobile (amber, blink)
+-       low: green:mobile & red:mobile (amber, turned on)
+-      good: green:mobile (turned on)
+
+Suggested-by: Hauke Mehrtens <hauke@hauke-m.de>
+Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
+---
+ include/dt-bindings/leds/common.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/dt-bindings/leds/common.h
++++ b/include/dt-bindings/leds/common.h
+@@ -90,6 +90,7 @@
+ #define LED_FUNCTION_INDICATOR "indicator"
+ #define LED_FUNCTION_LAN "lan"
+ #define LED_FUNCTION_MAIL "mail"
++#define LED_FUNCTION_MOBILE "mobile"
+ #define LED_FUNCTION_MTD "mtd"
+ #define LED_FUNCTION_PANIC "panic"
+ #define LED_FUNCTION_PROGRAMMING "programming"
diff --git a/target/linux/generic/pending-6.6/880-02-dt-bindings-leds-add-LED_FUNCTION_SPEED_-for-link-sp.patch b/target/linux/generic/pending-6.6/880-02-dt-bindings-leds-add-LED_FUNCTION_SPEED_-for-link-sp.patch
new file mode 100644 (file)
index 0000000..ab27cd3
--- /dev/null
@@ -0,0 +1,37 @@
+From e22afe910afcfb51b6ba6a0ae776939959727f54 Mon Sep 17 00:00:00 2001
+From: INAGAKI Hiroshi <musashino.open@gmail.com>
+Date: Wed, 20 Mar 2024 15:59:06 +0900
+Subject: [PATCH v2 2/2] dt-bindings: leds: add LED_FUNCTION_SPEED_* for link
+ speed on LAN/WAN
+
+Add LED_FUNCTION_SPEED_LAN and LED_FUNCTION_SPEED_WAN for LEDs that
+indicate link speed of ethernet ports on LAN/WAN. This is useful to
+distinguish those LEDs from LEDs that indicate link status (up/down).
+
+example:
+
+Fortinet FortiGate 30E/50E have LEDs that indicate link speed on each
+of the ethernet ports in addition to LEDs that indicate link status
+(up/down).
+
+- 1000 Mbps: green:speed-(lan|wan)-N
+-  100 Mbps: amber:speed-(lan|wan)-N
+-   10 Mbps: (none, turned off)
+
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
+---
+ include/dt-bindings/leds/common.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/include/dt-bindings/leds/common.h
++++ b/include/dt-bindings/leds/common.h
+@@ -96,6 +96,8 @@
+ #define LED_FUNCTION_PROGRAMMING "programming"
+ #define LED_FUNCTION_RX "rx"
+ #define LED_FUNCTION_SD "sd"
++#define LED_FUNCTION_SPEED_LAN "speed-lan"
++#define LED_FUNCTION_SPEED_WAN "speed-wan"
+ #define LED_FUNCTION_STANDBY "standby"
+ #define LED_FUNCTION_TORCH "torch"
+ #define LED_FUNCTION_TX "tx"
diff --git a/target/linux/generic/pending-6.6/890-usb-serial-add-support-for-CH348.patch b/target/linux/generic/pending-6.6/890-usb-serial-add-support-for-CH348.patch
new file mode 100644 (file)
index 0000000..028ecf4
--- /dev/null
@@ -0,0 +1,783 @@
+From df1357358eec062241bddd2995e7ef0ce86cf45a Mon Sep 17 00:00:00 2001
+X-Patchwork-Submitter: Corentin Labbe <clabbe@baylibre.com>
+X-Patchwork-Id: 13656881
+Message-Id: <20240507131522.3546113-2-clabbe@baylibre.com>
+X-Mailer: git-send-email 2.25.1
+In-Reply-To: <20240507131522.3546113-1-clabbe@baylibre.com>
+References: <20240507131522.3546113-1-clabbe@baylibre.com>
+Precedence: bulk
+X-Mailing-List: linux-usb@vger.kernel.org
+List-Id: <linux-usb.vger.kernel.org>
+From: Corentin Labbe <clabbe@baylibre.com>
+Date: Tue, 7 May 2024 13:15:22 +0000
+Subject: [PATCH v7] usb: serial: add support for CH348
+
+The CH348 is an USB octo port serial adapter.
+The device multiplexes all 8 ports in the same pair of Bulk endpoints.
+Since there is no public datasheet, unfortunately it remains some magic values
+
+Signed-off-by: Corentin Labbe <clabbe@baylibre.com>
+Tested-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+---
+ drivers/usb/serial/Kconfig  |   9 +
+ drivers/usb/serial/Makefile |   1 +
+ drivers/usb/serial/ch348.c  | 725 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 735 insertions(+)
+ create mode 100644 drivers/usb/serial/ch348.c
+
+--- a/drivers/usb/serial/Kconfig
++++ b/drivers/usb/serial/Kconfig
+@@ -112,6 +112,15 @@ config USB_SERIAL_CH341
+         To compile this driver as a module, choose M here: the
+         module will be called ch341.
++config USB_SERIAL_CH348
++      tristate "USB Winchiphead CH348 Octo Port Serial Driver"
++      help
++        Say Y here if you want to use a Winchiphead CH348 octo port
++        USB to serial adapter.
++
++        To compile this driver as a module, choose M here: the
++        module will be called ch348.
++
+ config USB_SERIAL_WHITEHEAT
+       tristate "USB ConnectTech WhiteHEAT Serial Driver"
+       select USB_EZUSB_FX2
+--- a/drivers/usb/serial/Makefile
++++ b/drivers/usb/serial/Makefile
+@@ -15,6 +15,7 @@ obj-$(CONFIG_USB_SERIAL_AIRCABLE)            += ai
+ obj-$(CONFIG_USB_SERIAL_ARK3116)              += ark3116.o
+ obj-$(CONFIG_USB_SERIAL_BELKIN)                       += belkin_sa.o
+ obj-$(CONFIG_USB_SERIAL_CH341)                        += ch341.o
++obj-$(CONFIG_USB_SERIAL_CH348)                        += ch348.o
+ obj-$(CONFIG_USB_SERIAL_CP210X)                       += cp210x.o
+ obj-$(CONFIG_USB_SERIAL_CYBERJACK)            += cyberjack.o
+ obj-$(CONFIG_USB_SERIAL_CYPRESS_M8)           += cypress_m8.o
+--- /dev/null
++++ b/drivers/usb/serial/ch348.c
+@@ -0,0 +1,725 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * USB serial driver for USB to Octal UARTs chip ch348.
++ *
++ * Copyright (C) 2022 Corentin Labbe <clabbe@baylibre.com>
++ * With the help of Neil Armstrong <neil.armstrong@linaro.org>
++ * and the help of Martin Blumenstingl <martin.blumenstingl@googlemail.com>
++ */
++
++#include <linux/completion.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/overflow.h>
++#include <linux/serial.h>
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/tty_flip.h>
++#include <linux/usb.h>
++#include <linux/usb/serial.h>
++
++#define CH348_CMD_TIMEOUT   2000
++
++#define CH348_CTO_D   0x01
++#define CH348_CTO_R   0x02
++
++#define CH348_CTI_C   0x10
++#define CH348_CTI_DSR 0x20
++#define CH348_CTI_R   0x40
++#define CH348_CTI_DCD 0x80
++
++#define CH348_LO      0x02
++#define CH348_LP      0x04
++#define CH348_LF      0x08
++#define CH348_LB      0x10
++
++#define CMD_W_R               0xC0
++#define CMD_W_BR      0x80
++
++#define CMD_WB_E      0x90
++#define CMD_RB_E      0xC0
++
++#define M_NOR         0x00
++#define M_HF          0x03
++
++#define R_MOD         0x97
++#define R_IO_D                0x98
++#define R_IO_O                0x99
++#define R_IO_I                0x9b
++#define R_TM_O                0x9c
++#define R_INIT                0xa1
++
++#define R_C1          0x01
++#define R_C2          0x02
++#define R_C4          0x04
++#define R_C5          0x06
++
++#define R_II_B1               0x06
++#define R_II_B2               0x02
++#define R_II_B3               0x00
++
++#define CMD_VER               0x96
++
++#define CH348_RX_PORT_CHUNK_LENGTH    32
++#define CH348_RX_PORT_MAX_LENGTH      30
++
++struct ch348_rxbuf {
++      u8 port;
++      u8 length;
++      u8 data[CH348_RX_PORT_MAX_LENGTH];
++} __packed;
++
++struct ch348_txbuf {
++      u8 port;
++      __le16 length;
++      u8 data[];
++} __packed;
++
++#define CH348_TX_HDRSIZE offsetof(struct ch348_txbuf, data)
++
++struct ch348_initbuf {
++      u8 cmd;
++      u8 reg;
++      u8 port;
++      __be32 baudrate;
++      u8 format;
++      u8 paritytype;
++      u8 databits;
++      u8 rate;
++      u8 unknown;
++} __packed;
++
++#define CH348_MAXPORT 8
++
++/*
++ * The CH348 multiplexes rx & tx into a pair of Bulk USB endpoints for
++ * the 8 serial ports, and another pair of Bulk USB endpoints to
++ * set port settings and receive port status events.
++ *
++ * The USB serial cores ties every Bulk endpoints pairs to each ports,
++ * but in our case it will set port 0 with the rx/tx endpoints
++ * and port 1 with the setup/status endpoints.
++ *
++ * To still take advantage of the generic code, we (re-)initialize
++ * the USB serial port structure with the correct USB endpoint
++ * for read and write, and write proper process_read_urb()
++ * and prepare_write_buffer() to correctly (de-)multiplex data.
++ * Also we use a custom write() implementation to wait until the buffer
++ * has been fully transmitted to prevent TX buffer overruns.
++ */
++
++/*
++ * struct ch348_port - per-port information
++ * @uartmode:         UART port current mode
++ * @write_completion: completion event when the TX buffer has been written out
++ */
++struct ch348_port {
++      u8 uartmode;
++      struct completion write_completion;
++};
++
++/*
++ * struct ch348 - main container for all this driver information
++ * @udev:             pointer to the CH348 USB device
++ * @ports:            List of per-port information
++ * @serial:           pointer to the serial structure
++ * @write_lock:               protect against concurrent writes so we don't lose data
++ * @cmd_ep:           endpoint number for configure operations
++ * @status_urb:               URB for status
++ * @status_buffer:    buffer used by status_urb
++ */
++struct ch348 {
++      struct usb_device *udev;
++      struct ch348_port ports[CH348_MAXPORT];
++      struct usb_serial *serial;
++
++      struct mutex write_lock;
++
++      int cmd_ep;
++
++      struct urb *status_urb;
++      u8 status_buffer[];
++};
++
++struct ch348_magic {
++      u8 action;
++      u8 reg;
++      u8 control;
++} __packed;
++
++struct ch348_status_entry {
++      u8 portnum:4;
++      u8 unused:4;
++      u8 reg_iir;
++      union {
++              u8 lsr_signal;
++              u8 modem_signal;
++              u8 init_data[10];
++      };
++} __packed;
++
++static void ch348_process_status_urb(struct urb *urb)
++{
++      struct ch348_status_entry *status_entry;
++      struct ch348 *ch348 = urb->context;
++      int ret, status = urb->status;
++      struct usb_serial_port *port;
++      unsigned int i, status_len;
++
++      switch (status) {
++      case 0:
++              /* success */
++              break;
++      case -ECONNRESET:
++      case -ENOENT:
++      case -ESHUTDOWN:
++              /* this urb is terminated, clean up */
++              dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
++                      __func__, status);
++              return;
++      default:
++              dev_err(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
++                      __func__, status);
++              goto exit;
++      }
++
++      if (urb->actual_length < 3) {
++              dev_warn(&ch348->udev->dev,
++                       "Received too short status buffer with %u bytes\n",
++                       urb->actual_length);
++              goto exit;
++      }
++
++      for (i = 0; i < urb->actual_length;) {
++              status_entry = urb->transfer_buffer + i;
++
++              if (status_entry->portnum >= CH348_MAXPORT) {
++                      dev_warn(&ch348->udev->dev,
++                               "Invalid port %d in status entry\n",
++                               status_entry->portnum);
++                      break;
++              }
++
++              port = ch348->serial->port[status_entry->portnum];
++              status_len = 3;
++
++              if (!status_entry->reg_iir) {
++                      dev_dbg(&port->dev, "Ignoring status with zero reg_iir\n");
++              } else if (status_entry->reg_iir == R_INIT) {
++                      status_len = 12;
++              } else if ((status_entry->reg_iir & 0x0f) == R_II_B1) {
++                      if (status_entry->lsr_signal & CH348_LO)
++                              port->icount.overrun++;
++                      if (status_entry->lsr_signal & CH348_LP)
++                              port->icount.parity++;
++                      if (status_entry->lsr_signal & CH348_LF)
++                              port->icount.frame++;
++                      if (status_entry->lsr_signal & CH348_LF)
++                              port->icount.brk++;
++              } else if ((status_entry->reg_iir & 0x0f) == R_II_B2) {
++                      complete_all(&ch348->ports[status_entry->portnum].write_completion);
++              } else {
++                      dev_warn(&port->dev,
++                               "Unsupported status with reg_iir 0x%02x\n",
++                               status_entry->reg_iir);
++              }
++
++              usb_serial_debug_data(&port->dev, __func__, status_len,
++                                    urb->transfer_buffer + i);
++
++              i += status_len;
++      }
++
++exit:
++      ret = usb_submit_urb(urb, GFP_ATOMIC);
++      if (ret)
++              dev_err(&urb->dev->dev, "%s - usb_submit_urb failed; %d\n",
++                      __func__, ret);
++}
++
++/*
++ * Some values came from vendor tree, and we have no meaning for them, this
++ * function simply use them.
++ */
++static int ch348_do_magic(struct ch348 *ch348, int portnum, u8 action, u8 reg, u8 control)
++{
++      struct ch348_magic *buffer;
++      int ret, len;
++
++      buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
++      if (!buffer)
++              return -ENOMEM;
++
++      if (portnum < 4)
++              reg += 0x10 * portnum;
++      else
++              reg += 0x10 * (portnum - 4) + 0x08;
++
++      buffer->action = action;
++      buffer->reg = reg;
++      buffer->control = control;
++
++      ret = usb_bulk_msg(ch348->udev, ch348->cmd_ep, buffer, 3, &len,
++                         CH348_CMD_TIMEOUT);
++      if (ret)
++              dev_err(&ch348->udev->dev, "Failed to write magic err=%d\n", ret);
++
++      kfree(buffer);
++
++      return ret;
++}
++
++static int ch348_configure(struct ch348 *ch348, int portnum)
++{
++      int ret;
++
++      ret = ch348_do_magic(ch348, portnum, CMD_W_R, R_C2, 0x87);
++      if (ret)
++              return ret;
++
++      return ch348_do_magic(ch348, portnum, CMD_W_R, R_C4, 0x08);
++}
++
++static void ch348_process_read_urb(struct urb *urb)
++{
++      struct usb_serial_port *port = urb->context;
++      struct ch348 *ch348 = usb_get_serial_data(port->serial);
++      unsigned int portnum, usblen, i;
++      struct ch348_rxbuf *rxb;
++
++      if (urb->actual_length < 2) {
++              dev_dbg(&ch348->udev->dev, "Empty rx buffer\n");
++              return;
++      }
++
++      for (i = 0; i < urb->actual_length; i += CH348_RX_PORT_CHUNK_LENGTH) {
++              rxb = urb->transfer_buffer + i;
++              portnum = rxb->port;
++              if (portnum >= CH348_MAXPORT) {
++                      dev_dbg(&ch348->udev->dev, "Invalid port %d\n", portnum);
++                      break;
++              }
++
++              port = ch348->serial->port[portnum];
++
++              usblen = rxb->length;
++              if (usblen > CH348_RX_PORT_MAX_LENGTH) {
++                      dev_dbg(&port->dev, "Invalid length %d for port %d\n",
++                              usblen, portnum);
++                      break;
++              }
++
++              tty_insert_flip_string(&port->port, rxb->data, usblen);
++              tty_flip_buffer_push(&port->port);
++              port->icount.rx += usblen;
++              usb_serial_debug_data(&port->dev, __func__, usblen, rxb->data);
++      }
++}
++
++static int ch348_prepare_write_buffer(struct usb_serial_port *port, void *dest, size_t size)
++{
++      struct ch348_txbuf *rxt = dest;
++      int count;
++
++      count = kfifo_out_locked(&port->write_fifo, rxt->data,
++                               size - CH348_TX_HDRSIZE, &port->lock);
++
++      rxt->port = port->port_number;
++      rxt->length = cpu_to_le16(count);
++
++      return count + CH348_TX_HDRSIZE;
++}
++
++static int ch348_write(struct tty_struct *tty, struct usb_serial_port *port,
++                     const unsigned char *buf, int count)
++{
++      struct ch348 *ch348 = usb_get_serial_data(port->serial);
++      struct ch348_port *ch348_port = &ch348->ports[port->port_number];
++      int ret, max_tx_size;
++
++      if (tty_get_baud_rate(tty) < 9600 && count >= 128)
++              /*
++               * Writing larger buffers can take longer than the hardware
++               * allows before discarding the write buffer. Limit the
++               * transfer size in such cases.
++               * These values have been found by empirical testing.
++               */
++              max_tx_size = 128;
++      else
++              /*
++              * Only ingest as many bytes as we can transfer with one URB at
++              * a time. Once an URB has been written we need to wait for the
++              * R_II_B2 status event before we are allowed to send more data.
++              * If we ingest more data then usb_serial_generic_write() will
++              * internally try to process as much data as possible with any
++              * number of URBs without giving us the chance to wait in
++              * between transfers.
++              */
++              max_tx_size = port->bulk_out_size - CH348_TX_HDRSIZE;
++
++      reinit_completion(&ch348_port->write_completion);
++
++      mutex_lock(&ch348->write_lock);
++
++      /*
++       * For any (remaining) bytes that we did not transfer TTY core will
++       * call us again, with the buffer and count adjusted to the remaining
++       * data.
++       */
++      ret = usb_serial_generic_write(tty, port, buf, min(count, max_tx_size));
++
++      mutex_unlock(&ch348->write_lock);
++
++      if (ret <= 0)
++              return ret;
++
++      if (!wait_for_completion_interruptible_timeout(&ch348_port->write_completion,
++                                                     msecs_to_jiffies(CH348_CMD_TIMEOUT))) {
++              dev_err_console(port, "Failed to wait for TX buffer flush\n");
++              return -ETIMEDOUT;
++      }
++
++      return ret;
++}
++
++static int ch348_set_uartmode(struct ch348 *ch348, int portnum, u8 mode)
++{
++      int ret;
++
++      if (ch348->ports[portnum].uartmode == M_NOR && mode == M_HF) {
++              ret = ch348_do_magic(ch348, portnum, CMD_W_BR, R_C4, 0x51);
++              if (ret)
++                      return ret;
++              ch348->ports[portnum].uartmode = M_HF;
++      }
++
++      if (ch348->ports[portnum].uartmode == M_HF && mode == M_NOR) {
++              ret = ch348_do_magic(ch348, portnum, CMD_W_BR, R_C4, 0x50);
++              if (ret)
++                      return ret;
++              ch348->ports[portnum].uartmode = M_NOR;
++      }
++      return 0;
++}
++
++static void ch348_set_termios(struct tty_struct *tty, struct usb_serial_port *port,
++                            const struct ktermios *termios_old)
++{
++      struct ch348 *ch348 = usb_get_serial_data(port->serial);
++      struct ktermios *termios = &tty->termios;
++      int ret, portnum = port->port_number;
++      struct ch348_initbuf *buffer;
++      speed_t baudrate;
++      u8 format;
++
++      if (termios_old && !tty_termios_hw_change(&tty->termios, termios_old))
++              return;
++
++      buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
++      if (!buffer) {
++              if (termios_old)
++                      tty->termios = *termios_old;
++              return;
++      }
++
++      /*
++       * The datasheet states that only baud rates in range of 1200..6000000
++       * are supported. Tests however show that even baud rates as low as 50
++       * and as high as 12000000 are working in practice.
++       */
++      baudrate = clamp(tty_get_baud_rate(tty), 50, 12000000);
++
++      format = termios->c_cflag & CSTOPB ? 2 : 1;
++
++      buffer->paritytype = 0;
++      if (termios->c_cflag & PARENB) {
++              if (termios->c_cflag & PARODD)
++                      buffer->paritytype += 1;
++              else
++                      buffer->paritytype += 2;
++              if  (termios->c_cflag & CMSPAR)
++                      buffer->paritytype += 2;
++      }
++
++      switch (C_CSIZE(tty)) {
++      case CS5:
++              buffer->databits = 5;
++              break;
++      case CS6:
++              buffer->databits = 6;
++              break;
++      case CS7:
++              buffer->databits = 7;
++              break;
++      case CS8:
++      default:
++              buffer->databits = 8;
++              break;
++      }
++      buffer->cmd = CMD_WB_E | (portnum & 0x0F);
++      buffer->reg = R_INIT;
++      buffer->port = portnum;
++      buffer->baudrate = cpu_to_be32(baudrate);
++
++      if (format == 2)
++              buffer->format = 0x02;
++      else if (format == 1)
++              buffer->format = 0x00;
++
++      buffer->rate = max_t(speed_t, 5, (10000 * 15 / baudrate) + 1);
++
++      ret = usb_bulk_msg(ch348->udev, ch348->cmd_ep, buffer,
++                         sizeof(*buffer), NULL, CH348_CMD_TIMEOUT);
++      if (ret < 0) {
++              dev_err(&ch348->udev->dev, "Failed to change line settings: err=%d\n",
++                      ret);
++              goto out;
++      }
++
++      ret = ch348_do_magic(ch348, portnum, CMD_W_R, R_C1, 0x0F);
++      if (ret < 0)
++              goto out;
++
++      if (C_CRTSCTS(tty))
++              ret = ch348_set_uartmode(ch348, portnum, M_HF);
++      else
++              ret = ch348_set_uartmode(ch348, portnum, M_NOR);
++
++out:
++      kfree(buffer);
++}
++
++static int ch348_open(struct tty_struct *tty, struct usb_serial_port *port)
++{
++      struct ch348 *ch348 = usb_get_serial_data(port->serial);
++      int ret;
++
++      if (tty)
++              ch348_set_termios(tty, port, NULL);
++
++      ret = ch348_configure(ch348, port->port_number);
++      if (ret) {
++              dev_err(&ch348->udev->dev, "Fail to configure err=%d\n", ret);
++              return ret;
++      }
++
++      return usb_serial_generic_open(tty, port);
++}
++
++static int ch348_attach(struct usb_serial *serial)
++{
++      struct usb_endpoint_descriptor *epcmd, *epstatus;
++      struct usb_serial_port *port0 = serial->port[1];
++      struct usb_device *usb_dev = serial->dev;
++      int status_buffer_size, i, ret;
++      struct usb_interface *intf;
++      struct ch348 *ch348;
++
++      intf = usb_ifnum_to_if(usb_dev, 0);
++      epstatus = &intf->cur_altsetting->endpoint[2].desc;
++      epcmd = &intf->cur_altsetting->endpoint[3].desc;
++
++      status_buffer_size = usb_endpoint_maxp(epstatus);
++
++      ch348 = kzalloc(struct_size(ch348, status_buffer, status_buffer_size),
++                      GFP_KERNEL);
++      if (!ch348)
++              return -ENOMEM;
++
++      usb_set_serial_data(serial, ch348);
++
++      ch348->udev = serial->dev;
++      ch348->serial = serial;
++      mutex_init(&ch348->write_lock);
++
++      for (i = 0; i < CH348_MAXPORT; i++)
++              init_completion(&ch348->ports[i].write_completion);
++
++      ch348->status_urb = usb_alloc_urb(0, GFP_KERNEL);
++      if (!ch348->status_urb) {
++              ret = -ENOMEM;
++              goto err_free_ch348;
++      }
++
++      usb_fill_bulk_urb(ch348->status_urb, ch348->udev,
++                        usb_rcvbulkpipe(ch348->udev, epstatus->bEndpointAddress),
++                        ch348->status_buffer, status_buffer_size,
++                        ch348_process_status_urb, ch348);
++
++      ret = usb_submit_urb(ch348->status_urb, GFP_KERNEL);
++      if (ret) {
++              dev_err(&ch348->udev->dev,
++                      "%s - failed to submit status/interrupt urb %i\n",
++                      __func__, ret);
++              goto err_free_status_urb;
++      }
++
++      ret = usb_serial_generic_submit_read_urbs(port0, GFP_KERNEL);
++      if (ret)
++              goto err_kill_status_urb;
++
++      ch348->cmd_ep = usb_sndbulkpipe(usb_dev, epcmd->bEndpointAddress);
++
++      return 0;
++
++err_kill_status_urb:
++      usb_kill_urb(ch348->status_urb);
++err_free_status_urb:
++      usb_free_urb(ch348->status_urb);
++err_free_ch348:
++      kfree(ch348);
++      return ret;
++}
++
++static void ch348_release(struct usb_serial *serial)
++{
++      struct ch348 *ch348 = usb_get_serial_data(serial);
++
++      usb_kill_urb(ch348->status_urb);
++      usb_free_urb(ch348->status_urb);
++
++      kfree(ch348);
++}
++
++static void ch348_print_version(struct usb_serial *serial)
++{
++      u8 *version_buf;
++      int ret;
++
++      version_buf = kzalloc(4, GFP_KERNEL);
++      if (!version_buf)
++              return;
++
++      ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
++                            CMD_VER,
++                            USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
++                            0, 0, version_buf, 4, CH348_CMD_TIMEOUT);
++      if (ret < 0)
++              dev_dbg(&serial->dev->dev, "Failed to read CMD_VER: %d\n", ret);
++      else
++              dev_info(&serial->dev->dev, "Found WCH CH348%s\n",
++                       (version_buf[1] & 0x80) ? "Q" : "L");
++
++      kfree(version_buf);
++}
++
++static int ch348_probe(struct usb_serial *serial, const struct usb_device_id *id)
++{
++      struct usb_endpoint_descriptor *epread, *epwrite, *epstatus, *epcmd;
++      struct usb_device *usb_dev = serial->dev;
++      struct usb_interface *intf;
++      int ret;
++
++      intf = usb_ifnum_to_if(usb_dev, 0);
++
++      ret = usb_find_common_endpoints(intf->cur_altsetting, &epread, &epwrite,
++                                      NULL, NULL);
++      if (ret) {
++              dev_err(&serial->dev->dev, "Failed to find basic endpoints ret=%d\n", ret);
++              return ret;
++      }
++
++      epstatus = &intf->cur_altsetting->endpoint[2].desc;
++      if (!usb_endpoint_is_bulk_in(epstatus)) {
++              dev_err(&serial->dev->dev, "Missing second bulk in (STATUS/INT)\n");
++              return -ENODEV;
++      }
++
++      epcmd = &intf->cur_altsetting->endpoint[3].desc;
++      if (!usb_endpoint_is_bulk_out(epcmd)) {
++              dev_err(&serial->dev->dev, "Missing second bulk out (CMD)\n");
++              return -ENODEV;
++      }
++
++      ch348_print_version(serial);
++
++      return 0;
++}
++
++static int ch348_calc_num_ports(struct usb_serial *serial,
++                              struct usb_serial_endpoints *epds)
++{
++      int i;
++
++      for (i = 1; i < CH348_MAXPORT; ++i) {
++              epds->bulk_out[i] = epds->bulk_out[0];
++              epds->bulk_in[i] = epds->bulk_in[0];
++      }
++
++      epds->num_bulk_out = CH348_MAXPORT;
++      epds->num_bulk_in = CH348_MAXPORT;
++
++      return CH348_MAXPORT;
++}
++
++static int ch348_suspend(struct usb_serial *serial, pm_message_t message)
++{
++      struct ch348 *ch348 = usb_get_serial_data(serial);
++
++      usb_kill_urb(ch348->status_urb);
++
++      return 0;
++}
++
++static int ch348_resume(struct usb_serial *serial)
++{
++      struct ch348 *ch348 = usb_get_serial_data(serial);
++      int ret;
++
++      ret = usb_submit_urb(ch348->status_urb, GFP_KERNEL);
++      if (ret) {
++              dev_err(&ch348->udev->dev,
++                      "%s - failed to submit status/interrupt urb %i\n",
++                      __func__, ret);
++              return ret;
++      }
++
++      ret = usb_serial_generic_resume(serial);
++      if (ret)
++              usb_kill_urb(ch348->status_urb);
++
++      return ret;
++}
++
++static const struct usb_device_id ch348_ids[] = {
++      { USB_DEVICE(0x1a86, 0x55d9), },
++      { /* sentinel */ }
++};
++
++MODULE_DEVICE_TABLE(usb, ch348_ids);
++
++static struct usb_serial_driver ch348_device = {
++      .driver = {
++              .owner = THIS_MODULE,
++              .name = "ch348",
++      },
++      .id_table =             ch348_ids,
++      .num_ports =            CH348_MAXPORT,
++      .num_bulk_in =          1,
++      .num_bulk_out =         1,
++      .open =                 ch348_open,
++      .set_termios =          ch348_set_termios,
++      .process_read_urb =     ch348_process_read_urb,
++      .prepare_write_buffer = ch348_prepare_write_buffer,
++      .write =                ch348_write,
++      .probe =                ch348_probe,
++      .calc_num_ports =       ch348_calc_num_ports,
++      .attach =               ch348_attach,
++      .release =              ch348_release,
++      .suspend =              ch348_suspend,
++      .resume =               ch348_resume,
++};
++
++static struct usb_serial_driver * const serial_drivers[] = {
++      &ch348_device, NULL
++};
++
++module_usb_serial_driver(serial_drivers, ch348_ids);
++
++MODULE_AUTHOR("Corentin Labbe <clabbe@baylibre.com>");
++MODULE_DESCRIPTION("USB CH348 Octo port serial converter driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/pending-6.6/900-net-ag71xx-fix-qca9530-and-qca9550-mdio-probe.patch b/target/linux/generic/pending-6.6/900-net-ag71xx-fix-qca9530-and-qca9550-mdio-probe.patch
new file mode 100644 (file)
index 0000000..f2c197d
--- /dev/null
@@ -0,0 +1,25 @@
+From 440415703692af4548e836832ef0434e87fbc357 Mon Sep 17 00:00:00 2001
+From: Oskari Lemmela <oskari@lemmela.net>
+Date: Sat, 13 Jul 2024 18:56:59 +0300
+Subject: [PATCH] net: ag71xx: fix qca9530 and qca9550 mdio probe
+
+Newer QCA9530 and QCA9550 devices should use same div table as AR933X.
+
+Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver")
+Signed-off-by: Oskari Lemmela <oskari@lemmela.net>
+---
+ drivers/net/ethernet/atheros/ag71xx.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/atheros/ag71xx.c
++++ b/drivers/net/ethernet/atheros/ag71xx.c
+@@ -638,7 +638,8 @@ static int ag71xx_mdio_get_divider(struc
+       if (!ref_clock)
+               return -EINVAL;
+-      if (ag71xx_is(ag, AR9330) || ag71xx_is(ag, AR9340)) {
++      if (ag71xx_is(ag, AR9330) || ag71xx_is(ag, AR9340) ||
++          ag71xx_is(ag, QCA9530) || ag71xx_is(ag, QCA9550)) {
+               table = ar933x_mdio_div_table;
+               ndivs = ARRAY_SIZE(ar933x_mdio_div_table);
+       } else if (ag71xx_is(ag, AR7240)) {
diff --git a/target/linux/generic/pending-6.6/920-mangle_bootargs.patch b/target/linux/generic/pending-6.6/920-mangle_bootargs.patch
new file mode 100644 (file)
index 0000000..36e7c18
--- /dev/null
@@ -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
+@@ -1820,6 +1820,15 @@ config DEBUG_RSEQ
+         If unsure, say N.
++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
+@@ -608,6 +608,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
+@@ -897,6 +920,7 @@ void start_kernel(void)
+       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/target/linux/generic/pending-6.6/980-tools-thermal-tmon-Fix-compilation-warning-for-wrong.patch b/target/linux/generic/pending-6.6/980-tools-thermal-tmon-Fix-compilation-warning-for-wrong.patch
new file mode 100644 (file)
index 0000000..6a0a199
--- /dev/null
@@ -0,0 +1,51 @@
+From a7a94ca21ac0f347f683d33c72b4aab57ce5eec3 Mon Sep 17 00:00:00 2001
+From: Florian Eckert <fe@dev.tdt.de>
+Date: Mon, 20 Nov 2023 11:13:20 +0100
+Subject: [PATCH] tools/thermal/tmon: Fix compilation warning for wrong format
+
+The following warnings are shown during compilation:
+
+tui.c: In function 'show_cooling_device':
+ tui.c:216:40: warning: format '%d' expects argument of type 'int', but
+argument 7 has type 'long unsigned int' [-Wformat=]
+   216 |                         "%02d %12.12s%6d %6d",
+       |                                      ~~^
+       |                                        |
+       |                                        int
+       |                                      %6ld
+ ......
+   219 |                         ptdata.cdi[j].cur_state,
+       |                         ~~~~~~~~~~~~~~~~~~~~~~~
+       |                                      |
+       |                                      long unsigned int
+ tui.c:216:44: warning: format '%d' expects argument of type 'int', but
+argument 8 has type 'long unsigned int' [-Wformat=]
+   216 |                         "%02d %12.12s%6d %6d",
+       |                                          ~~^
+       |                                            |
+       |                                            int
+       |                                          %6ld
+ ......
+   220 |                         ptdata.cdi[j].max_state);
+       |                         ~~~~~~~~~~~~~~~~~~~~~~~
+       |                                      |
+       |                                      long unsigned int
+
+To fix this, the correct string format must be used for printing.
+
+Signed-off-by: Florian Eckert <fe@dev.tdt.de>
+---
+ tools/thermal/tmon/tui.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/tools/thermal/tmon/tui.c
++++ b/tools/thermal/tmon/tui.c
+@@ -213,7 +213,7 @@ void show_cooling_device(void)
+                * cooling device instances. skip unused idr.
+                */
+               mvwprintw(cooling_device_window, j + 2, 1,
+-                      "%02d %12.12s%6d %6d",
++                      "%02d %12.12s%6lu %6lu",
+                       ptdata.cdi[j].instance,
+                       ptdata.cdi[j].type,
+                       ptdata.cdi[j].cur_state,